From 029d31be6ac3a36055f8e684d944c5c814b6c210 Mon Sep 17 00:00:00 2001 From: Jarkko Inki Date: Tue, 28 May 2019 12:58:22 +0200 Subject: [PATCH 01/46] Added setting for data folder. --- icepaposc/dialog_settings.py | 20 +- icepaposc/settings.py | 9 +- icepaposc/ui/dialog_settings.ui | 343 ++++++++++++++++------------- icepaposc/ui/ui_dialog_settings.py | 50 +++-- icepaposc/window_main.py | 2 +- 5 files changed, 252 insertions(+), 172 deletions(-) diff --git a/icepaposc/dialog_settings.py b/icepaposc/dialog_settings.py index a6cb6a9..9b5a013 100644 --- a/icepaposc/dialog_settings.py +++ b/icepaposc/dialog_settings.py @@ -19,6 +19,7 @@ from PyQt4.QtGui import QDialog from PyQt4.QtGui import QDialogButtonBox +from PyQt4.QtGui import QFileDialog from ui.ui_dialog_settings import Ui_DialogSettings @@ -42,13 +43,16 @@ def __init__(self, parent, settings): self.ui.sbDumpRate.setValue(self.settings.dump_rate) self.ui.sbLenAxisX.setMinimum(self.settings.default_x_axis_length_min) self.ui.sbLenAxisX.setMaximum(self.settings.default_x_axis_length_max) - self.ui.sbLenAxisX.setValue(self.settings.default_x_axis_length) + self.ui.sbLenAxisX.setValue(self.settings.default_x_axis_len) + self.ui.leDataFolder.setText(self.settings.data_folder) self.apply_button.setDisabled(True) def _connect_signals(self): self.ui.sbSampleRate.valueChanged.connect(self._sample_rate_changed) self.ui.sbDumpRate.valueChanged.connect(self._dump_rate_changed) self.ui.sbLenAxisX.valueChanged.connect(self._x_axis_length_changed) + self.ui.btnOpenFolderDlg.clicked.connect(self._launch_folder_dialog) + self.ui.leDataFolder.editingFinished.connect(self._new_data_folder) self.apply_button.clicked.connect(self._apply) self.close_button.clicked.connect(self.close) @@ -66,17 +70,27 @@ def _x_axis_length_changed(self): def _check_button_state(self): eq = self.ui.sbSampleRate.value() == self.settings.sample_rate and \ self.ui.sbDumpRate.value() == self.settings.dump_rate and \ - self.ui.sbLenAxisX.value() == self.settings.default_x_axis_length + self.ui.sbLenAxisX.value() == self.settings.default_x_axis_len and \ + self.ui.leDataFolder.text() == self.settings.data_folder self.apply_button.setDisabled(eq) def _update_gui_rate(self): update_rate = self.ui.sbSampleRate.value() * self.ui.sbDumpRate.value() self.ui.leGuiUpdateRate.setText(str(update_rate)) + def _launch_folder_dialog(self): + folder_name = QFileDialog.getExistingDirectory() + self.ui.leDataFolder.setText(folder_name) + self._check_button_state() + + def _new_data_folder(self): + self._check_button_state() + def _apply(self): self.settings.sample_rate = self.ui.sbSampleRate.value() self.settings.dump_rate = self.ui.sbDumpRate.value() - self.settings.default_x_axis_length = self.ui.sbLenAxisX.value() + self.settings.default_x_axis_len = self.ui.sbLenAxisX.value() + self.settings.data_folder = self.ui.leDataFolder.text() self.settings.announce_update() self.apply_button.setDisabled(True) diff --git a/icepaposc/settings.py b/icepaposc/settings.py index f48bb5f..7c712a0 100644 --- a/icepaposc/settings.py +++ b/icepaposc/settings.py @@ -26,7 +26,7 @@ def __init__(self, parent, collector): self.parent = parent self.collector = collector - # Settings held by the collector side. + # Settings for collector. self.sample_rate_min = collector.tick_interval_min self.sample_rate_max = collector.tick_interval_max self.sample_rate = collector.tick_interval @@ -34,10 +34,13 @@ def __init__(self, parent, collector): self.dump_rate_max = collector.sample_buf_len_max self.dump_rate = collector.sample_buf_len - # Settings held by the GUI side. + # Settings for GUI. self.default_x_axis_length_min = 5 # [Seconds] self.default_x_axis_length_max = 3600 # [Seconds] - self.default_x_axis_length = 30 # [Seconds] + self.default_x_axis_len = 30 # [Seconds] + + # Settings for data storage. + self.data_folder = "" def announce_update(self): self.collector.tick_interval = self.sample_rate diff --git a/icepaposc/ui/dialog_settings.ui b/icepaposc/ui/dialog_settings.ui index fb0438f..1d714fa 100644 --- a/icepaposc/ui/dialog_settings.ui +++ b/icepaposc/ui/dialog_settings.ui @@ -2,161 +2,203 @@ DialogSettings - - Oscilloscope Settings + + + 0 + 0 + 559 + 292 + - - false + + IcePapOSC Settings - - - - 0 - 0 - - - - Data Collection - - - - - - GUI Update Rate [ms] - - - - - - - - 0 - 0 - - - - - 80 - 0 - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - false - - - - 0 - 0 - - - - - 80 - 0 - - - - - 80 - 16777215 - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Sample Rate [ms] - - - - - - - - 0 - 0 - - - - - 80 - 0 - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Dump Rate [samples/dump] - - - - - + + + + + Data Collection + + + false + + + + + + GUI Update Rate [ms] + + + + + + + + 0 + 0 + + + + + 80 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + + 0 + 0 + + + + + 80 + 0 + + + + + 80 + 16777215 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Sample Rate [ms] + + + + + + + + 0 + 0 + + + + + 80 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Dump Rate [samples/dump] + + + + + + + + + + X-axis + + + + + + Default Length [sec] + + + + + + + + 0 + 0 + + + + + 80 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + - - - - 0 - 0 - - - - - 16777215 - 170 - - - - X-axis - - - - - - Default Length [sec] - - - - - - - - 0 - 0 - - - - - 80 - 0 - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - + + + + + + 0 + 86 + + + + Folder for Saved Data (filename: IcePapOSC_<date>_<time>.osc) + + + + + + + 200 + 0 + + + + + + + + + 0 + 0 + + + + + 32 + 32 + + + + ... + + + + + + + @@ -166,6 +208,9 @@ QDialogButtonBox::Apply|QDialogButtonBox::Close + + true + diff --git a/icepaposc/ui/ui_dialog_settings.py b/icepaposc/ui/ui_dialog_settings.py index e0d2467..b27e2f9 100644 --- a/icepaposc/ui/ui_dialog_settings.py +++ b/icepaposc/ui/ui_dialog_settings.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'dialog_settings.ui' # -# Created: Fri Nov 23 06:42:47 2018 +# Created: Tue May 28 10:40:38 2019 # by: PyQt4 UI code generator 4.10.1 # # WARNING! All changes made in this file will be lost! @@ -26,15 +26,13 @@ def _translate(context, text, disambig): class Ui_DialogSettings(object): def setupUi(self, DialogSettings): DialogSettings.setObjectName(_fromUtf8("DialogSettings")) - DialogSettings.setModal(False) + DialogSettings.resize(559, 292) self.verticalLayout_2 = QtGui.QVBoxLayout(DialogSettings) self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2")) + self.hl1 = QtGui.QHBoxLayout() + self.hl1.setObjectName(_fromUtf8("hl1")) self.gbSampling = QtGui.QGroupBox(DialogSettings) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.gbSampling.sizePolicy().hasHeightForWidth()) - self.gbSampling.setSizePolicy(sizePolicy) + self.gbSampling.setChecked(False) self.gbSampling.setObjectName(_fromUtf8("gbSampling")) self.glDataCollection = QtGui.QGridLayout(self.gbSampling) self.glDataCollection.setObjectName(_fromUtf8("glDataCollection")) @@ -79,14 +77,8 @@ def setupUi(self, DialogSettings): self.labelDumpRate = QtGui.QLabel(self.gbSampling) self.labelDumpRate.setObjectName(_fromUtf8("labelDumpRate")) self.glDataCollection.addWidget(self.labelDumpRate, 1, 1, 1, 1) - self.verticalLayout_2.addWidget(self.gbSampling) + self.hl1.addWidget(self.gbSampling) self.gbXAxis = QtGui.QGroupBox(DialogSettings) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.gbXAxis.sizePolicy().hasHeightForWidth()) - self.gbXAxis.setSizePolicy(sizePolicy) - self.gbXAxis.setMaximumSize(QtCore.QSize(16777215, 170)) self.gbXAxis.setObjectName(_fromUtf8("gbXAxis")) self.glXAxis = QtGui.QGridLayout(self.gbXAxis) self.glXAxis.setObjectName(_fromUtf8("glXAxis")) @@ -103,10 +95,34 @@ def setupUi(self, DialogSettings): self.sbLenAxisX.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.sbLenAxisX.setObjectName(_fromUtf8("sbLenAxisX")) self.glXAxis.addWidget(self.sbLenAxisX, 0, 1, 1, 1) - self.verticalLayout_2.addWidget(self.gbXAxis) + self.hl1.addWidget(self.gbXAxis) + self.verticalLayout_2.addLayout(self.hl1) + self.hl2 = QtGui.QHBoxLayout() + self.hl2.setObjectName(_fromUtf8("hl2")) + self.gbDataFolder = QtGui.QGroupBox(DialogSettings) + self.gbDataFolder.setMinimumSize(QtCore.QSize(0, 86)) + self.gbDataFolder.setObjectName(_fromUtf8("gbDataFolder")) + self.gridLayout = QtGui.QGridLayout(self.gbDataFolder) + self.gridLayout.setObjectName(_fromUtf8("gridLayout")) + self.leDataFolder = QtGui.QLineEdit(self.gbDataFolder) + self.leDataFolder.setMinimumSize(QtCore.QSize(200, 0)) + self.leDataFolder.setObjectName(_fromUtf8("leDataFolder")) + self.gridLayout.addWidget(self.leDataFolder, 0, 0, 1, 1) + self.btnOpenFolderDlg = QtGui.QPushButton(self.gbDataFolder) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnOpenFolderDlg.sizePolicy().hasHeightForWidth()) + self.btnOpenFolderDlg.setSizePolicy(sizePolicy) + self.btnOpenFolderDlg.setMaximumSize(QtCore.QSize(32, 32)) + self.btnOpenFolderDlg.setObjectName(_fromUtf8("btnOpenFolderDlg")) + self.gridLayout.addWidget(self.btnOpenFolderDlg, 0, 1, 1, 1) + self.hl2.addWidget(self.gbDataFolder) + self.verticalLayout_2.addLayout(self.hl2) self.bbApplyClose = QtGui.QDialogButtonBox(DialogSettings) self.bbApplyClose.setOrientation(QtCore.Qt.Horizontal) self.bbApplyClose.setStandardButtons(QtGui.QDialogButtonBox.Apply|QtGui.QDialogButtonBox.Close) + self.bbApplyClose.setCenterButtons(True) self.bbApplyClose.setObjectName(_fromUtf8("bbApplyClose")) self.verticalLayout_2.addWidget(self.bbApplyClose) @@ -116,11 +132,13 @@ def setupUi(self, DialogSettings): QtCore.QMetaObject.connectSlotsByName(DialogSettings) def retranslateUi(self, DialogSettings): - DialogSettings.setWindowTitle(_translate("DialogSettings", "Oscilloscope Settings", None)) + DialogSettings.setWindowTitle(_translate("DialogSettings", "IcePapOSC Settings", None)) self.gbSampling.setTitle(_translate("DialogSettings", "Data Collection", None)) self.labelGuiRate.setText(_translate("DialogSettings", "GUI Update Rate [ms]", None)) self.labelSampleRate.setText(_translate("DialogSettings", "Sample Rate [ms]", None)) self.labelDumpRate.setText(_translate("DialogSettings", "Dump Rate [samples/dump]", None)) self.gbXAxis.setTitle(_translate("DialogSettings", "X-axis", None)) self.labelXAxisLength.setText(_translate("DialogSettings", "Default Length [sec]", None)) + self.gbDataFolder.setTitle(_translate("DialogSettings", "Folder for Saved Data (filename: IcePapOSC__ diff --git a/icepaposc/ui/ui_dialog_settings.py b/icepaposc/ui/ui_dialog_settings.py index b27e2f9..e7e27da 100644 --- a/icepaposc/ui/ui_dialog_settings.py +++ b/icepaposc/ui/ui_dialog_settings.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'dialog_settings.ui' # -# Created: Tue May 28 10:40:38 2019 +# Created: Wed May 29 08:55:21 2019 # by: PyQt4 UI code generator 4.10.1 # # WARNING! All changes made in this file will be lost! @@ -115,6 +115,7 @@ def setupUi(self, DialogSettings): sizePolicy.setHeightForWidth(self.btnOpenFolderDlg.sizePolicy().hasHeightForWidth()) self.btnOpenFolderDlg.setSizePolicy(sizePolicy) self.btnOpenFolderDlg.setMaximumSize(QtCore.QSize(32, 32)) + self.btnOpenFolderDlg.setAutoDefault(False) self.btnOpenFolderDlg.setObjectName(_fromUtf8("btnOpenFolderDlg")) self.gridLayout.addWidget(self.btnOpenFolderDlg, 0, 1, 1, 1) self.hl2.addWidget(self.gbDataFolder) From 0bc44e91267b86387478109ba658ba6e018a0d20 Mon Sep 17 00:00:00 2001 From: Jarkko Inki Date: Wed, 29 May 2019 13:53:55 +0200 Subject: [PATCH 03/46] Added meny item Save_to_File. --- icepaposc/ui/ui_window_main.py | 7 ++++++- icepaposc/ui/window_main.ui | 9 +++++++++ icepaposc/window_main.py | 4 ++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/icepaposc/ui/ui_window_main.py b/icepaposc/ui/ui_window_main.py index 6f76a4f..a8c4e09 100644 --- a/icepaposc/ui/ui_window_main.py +++ b/icepaposc/ui/ui_window_main.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'window_main.ui' # -# Created: Thu May 9 10:31:06 2019 +# Created: Wed May 29 11:59:40 2019 # by: PyQt4 UI code generator 4.10.1 # # WARNING! All changes made in this file will be lost! @@ -264,7 +264,10 @@ def setupUi(self, WindowMain): self.actionTarget.setObjectName(_fromUtf8("actionTarget")) self.actionAdd_Signals = QtGui.QAction(WindowMain) self.actionAdd_Signals.setObjectName(_fromUtf8("actionAdd_Signals")) + self.actionSave_to_File = QtGui.QAction(WindowMain) + self.actionSave_to_File.setObjectName(_fromUtf8("actionSave_to_File")) self.menuFile.addAction(self.actionAdd_Signals) + self.menuFile.addAction(self.actionSave_to_File) self.menuFile.addAction(self.actionSettings) self.menuFile.addAction(self.actionExit) self.menuSignal_Sets.addAction(self.actionClosed_Loop) @@ -309,4 +312,6 @@ def retranslateUi(self, WindowMain): self.actionTarget.setText(_translate("WindowMain", "Target", None)) self.actionAdd_Signals.setText(_translate("WindowMain", "Add Signals", None)) self.actionAdd_Signals.setShortcut(_translate("WindowMain", "Ctrl+A", None)) + self.actionSave_to_File.setText(_translate("WindowMain", "Save to File", None)) + self.actionSave_to_File.setShortcut(_translate("WindowMain", "Ctrl+F", None)) diff --git a/icepaposc/ui/window_main.ui b/icepaposc/ui/window_main.ui index 127c18e..6b065b0 100644 --- a/icepaposc/ui/window_main.ui +++ b/icepaposc/ui/window_main.ui @@ -541,6 +541,7 @@ File + @@ -592,6 +593,14 @@ Ctrl+A + + + Save to File + + + Ctrl+F + + diff --git a/icepaposc/window_main.py b/icepaposc/window_main.py index ee2dc46..6afea6c 100644 --- a/icepaposc/window_main.py +++ b/icepaposc/window_main.py @@ -168,6 +168,7 @@ def _connect_signals(self): self.ui.btnResetY.clicked.connect(self._enable_auto_range_y) self.ui.btnPause.clicked.connect(self._pause_x_axis) self.ui.btnNow.clicked.connect(self._goto_now) + self.ui.actionSave_to_File.triggered.connect(self._save_to_file) self.ui.actionSettings.triggered.connect(self._display_settings_dlg) self.ui.actionExit.triggered.connect(self.close) self.ui.actionClosed_Loop.triggered.connect(self._signals_closed_loop) @@ -429,6 +430,9 @@ def enable_action(self, enable=True): """Enables or disables menu item File|Settings.""" self.ui.actionSettings.setEnabled(enable) + def _save_to_file(self): + pass + def _display_settings_dlg(self): self.enable_action(False) dlg = DialogSettings(self, self.settings) From 18921301258674e8545273f4eaa6b38a0ede7d8d Mon Sep 17 00:00:00 2001 From: Jarkko Inki Date: Tue, 18 Jun 2019 10:44:26 +0200 Subject: [PATCH 04/46] tmp commit --- icepaposc/curve_item.py | 8 ++++---- icepaposc/ui/ui_window_main.py | 6 +++++- icepaposc/ui/window_main.ui | 7 +++++++ icepaposc/window_main.py | 22 +++++++++++++++++++++- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/icepaposc/curve_item.py b/icepaposc/curve_item.py index db874b2..07974c8 100644 --- a/icepaposc/curve_item.py +++ b/icepaposc/curve_item.py @@ -108,8 +108,8 @@ def create_curve(self): def update_curve(self, time_min, time_max): """Updates the curve with recent collected data.""" with self.lock: - idx_min = self._get_time_index(time_min) - idx_max = self._get_time_index(time_max) + idx_min = self.get_time_index(time_min) + idx_max = self.get_time_index(time_max) self.curve.setData(x=self.array_time[idx_min:idx_max], y=self.array_val[idx_min:idx_max]) @@ -159,14 +159,14 @@ def get_y(self, time_val): Return: Signal value corresponding to an adjacent sample in time. """ with self.lock: - idx = self._get_time_index(time_val) + idx = self.get_time_index(time_val) return self.array_val[idx] def clear(self): self.array_time[:] = [] self.array_val[:] = [] - def _get_time_index(self, time_val): + def get_time_index(self, time_val): """ Retrieve the sample index corresponding to the provided time value. diff --git a/icepaposc/ui/ui_window_main.py b/icepaposc/ui/ui_window_main.py index a8c4e09..1ba37d9 100644 --- a/icepaposc/ui/ui_window_main.py +++ b/icepaposc/ui/ui_window_main.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'window_main.ui' # -# Created: Wed May 29 11:59:40 2019 +# Created: Mon Jun 17 14:22:10 2019 # by: PyQt4 UI code generator 4.10.1 # # WARNING! All changes made in this file will be lost! @@ -235,6 +235,9 @@ def setupUi(self, WindowMain): self.btnNow = QtGui.QPushButton(self.centralwidget) self.btnNow.setObjectName(_fromUtf8("btnNow")) self.hloCurveButtons.addWidget(self.btnNow) + self.btnSave = QtGui.QPushButton(self.centralwidget) + self.btnSave.setObjectName(_fromUtf8("btnSave")) + self.hloCurveButtons.addWidget(self.btnSave) spacerItem2 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.hloCurveButtons.addItem(spacerItem2) self.vloCurves.addLayout(self.hloCurveButtons) @@ -302,6 +305,7 @@ def retranslateUi(self, WindowMain): self.btnResetY.setText(_translate("WindowMain", "Reset Y", None)) self.btnResetX.setText(_translate("WindowMain", "Reset X", None)) self.btnNow.setText(_translate("WindowMain", "Now", None)) + self.btnSave.setText(_translate("WindowMain", "Save", None)) self.menuFile.setTitle(_translate("WindowMain", "File", None)) self.menuSignal_Sets.setTitle(_translate("WindowMain", "Signal Sets", None)) self.actionExit.setText(_translate("WindowMain", "Exit", None)) diff --git a/icepaposc/ui/window_main.ui b/icepaposc/ui/window_main.ui index 6b065b0..e9b1e4f 100644 --- a/icepaposc/ui/window_main.ui +++ b/icepaposc/ui/window_main.ui @@ -506,6 +506,13 @@ + + + + Save + + + diff --git a/icepaposc/window_main.py b/icepaposc/window_main.py index 6afea6c..1fbe9db 100644 --- a/icepaposc/window_main.py +++ b/icepaposc/window_main.py @@ -19,6 +19,7 @@ from PyQt4 import QtGui from PyQt4.QtCore import Qt +from PyQt4.QtGui import QFileDialog from ui.ui_window_main import Ui_WindowMain from collector import Collector from dialog_settings import DialogSettings @@ -26,6 +27,7 @@ from axis_time import AxisTime from curve_item import CurveItem import pyqtgraph as pg +import pandas as pd import time @@ -168,6 +170,7 @@ def _connect_signals(self): self.ui.btnResetY.clicked.connect(self._enable_auto_range_y) self.ui.btnPause.clicked.connect(self._pause_x_axis) self.ui.btnNow.clicked.connect(self._goto_now) + self.ui.btnSave.clicked.connect(self._save_to_file) self.ui.actionSave_to_File.triggered.connect(self._save_to_file) self.ui.actionSettings.triggered.connect(self._display_settings_dlg) self.ui.actionExit.triggered.connect(self.close) @@ -431,7 +434,24 @@ def enable_action(self, enable=True): self.ui.actionSettings.setEnabled(enable) def _save_to_file(self): - pass + if not self.curve_items: + return + fn = QFileDialog.getSaveFileName(filter="*.csv") + if not fn: + return + try: + open(fn, "w+") + except Exception as e: + msg = 'Failed to open/create file: {}\n{}'.format(fn, e) + print(msg) + QtGui.QMessageBox.critical(None, 'File Open Failed', msg) + return + self._create_csv_file(fn) + + def _create_csv_file(self, file_name): + for ci in self.curve_items: + pass + df.to_csv(file_name) def _display_settings_dlg(self): self.enable_action(False) From 3d873561490a09ee1ba2cee2b5b8a54f369faead Mon Sep 17 00:00:00 2001 From: Jarkko Inki Date: Wed, 19 Jun 2019 10:15:25 +0200 Subject: [PATCH 05/46] Added support for saving data to file. --- icepaposc/curve_item.py | 8 +++---- icepaposc/window_main.py | 47 ++++++++++++++++++++++++++++------------ 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/icepaposc/curve_item.py b/icepaposc/curve_item.py index 07974c8..db874b2 100644 --- a/icepaposc/curve_item.py +++ b/icepaposc/curve_item.py @@ -108,8 +108,8 @@ def create_curve(self): def update_curve(self, time_min, time_max): """Updates the curve with recent collected data.""" with self.lock: - idx_min = self.get_time_index(time_min) - idx_max = self.get_time_index(time_max) + idx_min = self._get_time_index(time_min) + idx_max = self._get_time_index(time_max) self.curve.setData(x=self.array_time[idx_min:idx_max], y=self.array_val[idx_min:idx_max]) @@ -159,14 +159,14 @@ def get_y(self, time_val): Return: Signal value corresponding to an adjacent sample in time. """ with self.lock: - idx = self.get_time_index(time_val) + idx = self._get_time_index(time_val) return self.array_val[idx] def clear(self): self.array_time[:] = [] self.array_val[:] = [] - def get_time_index(self, time_val): + def _get_time_index(self, time_val): """ Retrieve the sample index corresponding to the provided time value. diff --git a/icepaposc/window_main.py b/icepaposc/window_main.py index ed124b1..363f9a4 100644 --- a/icepaposc/window_main.py +++ b/icepaposc/window_main.py @@ -17,9 +17,12 @@ # along with IcepapOCS. If not, see . # ----------------------------------------------------------------------------- -from PyQt4 import QtGui -from PyQt4.QtCore import Qt +from PyQt4.QtGui import QMainWindow from PyQt4.QtGui import QFileDialog +from PyQt4.QtGui import QMessageBox +from PyQt4.QtGui import QBoxLayout +from PyQt4.QtGui import QColor +from PyQt4.QtCore import Qt from ui.ui_window_main import Ui_WindowMain from collector import Collector from dialog_settings import DialogSettings @@ -27,11 +30,13 @@ from axis_time import AxisTime from curve_item import CurveItem import pyqtgraph as pg +import numpy as np import pandas as pd +import collections import time -class WindowMain(QtGui.QMainWindow): +class WindowMain(QMainWindow): """A dialog for plotting IcePAP signals.""" def __init__(self, host, port, timeout, siglist, selected_driver=None): @@ -46,7 +51,7 @@ def __init__(self, host, port, timeout, siglist, selected_driver=None): Example: ["1:PosAxis:1", "1:MeasI:2", "1:MeasVm:3"] selected_driver - The driver to display in combobox at startup. """ - QtGui.QMainWindow.__init__(self, None) + QMainWindow.__init__(self, None) self.ui = Ui_WindowMain() self.setAttribute(Qt.WA_DeleteOnClose, True) self.ui.setupUi(self) @@ -60,7 +65,7 @@ def __init__(self, host, port, timeout, siglist, selected_driver=None): except Exception as e: msg = 'Failed to create main window.\n{}'.format(e) print(msg) - QtGui.QMessageBox.critical(None, 'Create Main Window', msg) + QMessageBox.critical(None, 'Create Main Window', msg) return self.subscriptions = {} @@ -74,7 +79,7 @@ def __init__(self, host, port, timeout, siglist, selected_driver=None): self.view_boxes = [self.plot_widget.getViewBox(), pg.ViewBox(), pg.ViewBox()] - self.ui.vloCurves.setDirection(QtGui.QBoxLayout.BottomToTop) + self.ui.vloCurves.setDirection(QBoxLayout.BottomToTop) self.ui.vloCurves.addWidget(self.plot_widget) # Set up the X-axis. @@ -127,7 +132,7 @@ def __init__(self, host, port, timeout, siglist, selected_driver=None): 'It should be: ' \ '::'.format(sig) print(msg) - QtGui.QMessageBox.critical(None, 'Bad Signal Syntax', msg) + QMessageBox.critical(None, 'Bad Signal Syntax', msg) return self._add_signal(int(lst[0]), lst[1], int(lst[2])) @@ -147,7 +152,7 @@ def _fill_combo_box_signals(self): msg = 'Internal error!\nNew signals added.\nAdd ' \ 'more colors and pens.' print(msg) - QtGui.QMessageBox.warning(None, 'Available Signals', msg) + QMessageBox.warning(None, 'Available Signals', msg) for i in range(num_colors): self.ui.cbSignals.addItem(signals[i]) else: @@ -250,7 +255,7 @@ def _add_signal(self, driver_addr, signal_name, y_axis): msg = 'Failed to subscribe to signal {} ' \ 'from driver {}.\n{}'.format(signal_name, driver_addr, e) print(msg) - QtGui.QMessageBox.critical(None, 'Add Curve', msg) + QMessageBox.critical(None, 'Add Curve', msg) return try: color_idx = self.collector.get_signal_index(signal_name) @@ -258,7 +263,7 @@ def _add_signal(self, driver_addr, signal_name, y_axis): msg = 'Internal error. Failed to retrieve index ' \ 'for signal {}.\n{}'.format(signal_name, e) print(msg) - QtGui.QMessageBox.critical(None, 'Add Curve', msg) + QMessageBox.critical(None, 'Add Curve', msg) return ci = CurveItem(subscription_id, driver_addr, signal_name, y_axis, color_idx) @@ -269,7 +274,7 @@ def _add_signal(self, driver_addr, signal_name, y_axis): index = len(self.curve_items) - 1 self.ui.lvActiveSig.setCurrentRow(index) self.ui.lvActiveSig.item(index).setForeground(ci.color) - self.ui.lvActiveSig.item(index).setBackground(QtGui.QColor(0, 0, 0)) + self.ui.lvActiveSig.item(index).setBackground(QColor(0, 0, 0)) self._update_plot_axes_labels() self._update_button_status() @@ -304,7 +309,7 @@ def _shift_button_clicked(self): self.ui.lvActiveSig.takeItem(index) self.ui.lvActiveSig.insertItem(index, ci.signature) self.ui.lvActiveSig.item(index).setForeground(ci.color) - self.ui.lvActiveSig.item(index).setBackground(QtGui.QColor(0, 0, 0)) + self.ui.lvActiveSig.item(index).setBackground(QColor(0, 0, 0)) self.ui.lvActiveSig.setCurrentRow(index) self._update_plot_axes_labels() @@ -441,18 +446,32 @@ def _save_to_file(self): fn = QFileDialog.getSaveFileName(filter="*.csv") if not fn: return + if fn[-4:] != ".csv": + fn = fn + ".csv" try: open(fn, "w+") except Exception as e: msg = 'Failed to open/create file: {}\n{}'.format(fn, e) print(msg) - QtGui.QMessageBox.critical(None, 'File Open Failed', msg) + QMessageBox.critical(None, 'File Open Failed', msg) return self._create_csv_file(fn) def _create_csv_file(self, file_name): + my_dict = collections.OrderedDict() for ci in self.curve_items: - pass + header = "time-{}-{}".format(ci.driver_addr, ci.signal_name) + my_dict[header] = ci.array_time + header = "val-{}-{}".format(ci.driver_addr, ci.signal_name) + my_dict[header] = ci.array_val + key_longest = my_dict.keys()[0] + for key in my_dict: + if my_dict[key][0] < my_dict[key_longest][0]: + key_longest = key + for key in my_dict: + delta = len(my_dict[key_longest]) - len(my_dict[key]) + my_dict[key] = delta * [np.nan] + my_dict[key] + df = pd.DataFrame(data=my_dict) df.to_csv(file_name) def _display_settings_dlg(self): From 669358e2f37c7cb0650028fa86e8883d9ba3bcbf Mon Sep 17 00:00:00 2001 From: Jarkko Inki Date: Wed, 19 Jun 2019 12:36:46 +0200 Subject: [PATCH 06/46] Removed magic text. --- icepaposc/window_main.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/icepaposc/window_main.py b/icepaposc/window_main.py index 363f9a4..d417f93 100644 --- a/icepaposc/window_main.py +++ b/icepaposc/window_main.py @@ -443,11 +443,12 @@ def enable_action(self, enable=True): def _save_to_file(self): if not self.curve_items: return - fn = QFileDialog.getSaveFileName(filter="*.csv") + extension = ".csv" + fn = QFileDialog.getSaveFileName(filter=extension) if not fn: return - if fn[-4:] != ".csv": - fn = fn + ".csv" + if fn[-4:] != extension: + fn = fn + extension try: open(fn, "w+") except Exception as e: From 76284669082edf93409a2512066406925415d9f6 Mon Sep 17 00:00:00 2001 From: Jarkko Inki Date: Thu, 20 Jun 2019 08:24:01 +0200 Subject: [PATCH 07/46] Settings are now saved to file. --- icepaposc/collector.py | 15 +++------ icepaposc/dialog_settings.py | 2 +- icepaposc/settings.py | 64 +++++++++++++++++++++++++----------- icepaposc/window_main.py | 3 +- 4 files changed, 52 insertions(+), 32 deletions(-) diff --git a/icepaposc/collector.py b/icepaposc/collector.py index ff77214..00534a0 100644 --- a/icepaposc/collector.py +++ b/icepaposc/collector.py @@ -28,7 +28,7 @@ class Collector: """Feeds a subscriber with collected IcePAP signal data.""" - def __init__(self, host, port, timeout, callback): + def __init__(self, host, port, timeout, settings, callback): """ Initializes an instance of class Collector. @@ -78,6 +78,7 @@ def __init__(self, host, port, timeout, callback): ) self.host = host self.port = port + self.settings = settings self.cb = callback self.icepap_system = None self.channels_subscribed = {} @@ -99,15 +100,9 @@ def __init__(self, host, port, timeout, callback): 'Aborting.'.format(self.host) raise Exception(msg) - self.tick_interval_min = 10 # [milliseconds] - self.tick_interval_max = 1000 # [milliseconds] - self.tick_interval = 50 # [milliseconds] - self.sample_buf_len_min = 1 - self.sample_buf_len_max = 100 - self.sample_buf_len = 2 self.ticker = QTimer() self.ticker.timeout.connect(self._tick) - self.ticker.start(self.tick_interval) + self.ticker.start(self.settings.sample_rate) def get_available_drivers(self): """ @@ -211,10 +206,10 @@ def _tick(self): continue tv = (time.time(), val) channel.collected_samples.append(tv) - if len(channel.collected_samples) >= self.sample_buf_len: + if len(channel.collected_samples) >= self.settings.dump_rate: self.cb(subscription_id, channel.collected_samples) channel.collected_samples = [] - self.ticker.start(self.tick_interval) + self.ticker.start(self.settings.sample_rate) def _getter_pos_axis(self, addr): return self.icepap_system[addr].pos diff --git a/icepaposc/dialog_settings.py b/icepaposc/dialog_settings.py index 53e3963..d7720ec 100644 --- a/icepaposc/dialog_settings.py +++ b/icepaposc/dialog_settings.py @@ -93,7 +93,7 @@ def _apply(self): self.settings.dump_rate = self.ui.sbDumpRate.value() self.settings.default_x_axis_len = self.ui.sbLenAxisX.value() self.settings.data_folder = data_folder - self.settings.announce_update() + self.settings.update() self.apply_button.setDisabled(True) @staticmethod diff --git a/icepaposc/settings.py b/icepaposc/settings.py index f7fb826..d60e09b 100644 --- a/icepaposc/settings.py +++ b/icepaposc/settings.py @@ -18,34 +18,58 @@ # ----------------------------------------------------------------------------- +from os.path import exists from os.path import expanduser +from ConfigParser import SafeConfigParser class Settings: """Application settings.""" - def __init__(self, parent, collector): + def __init__(self): """Initializes an instance of class Settings.""" - self.parent = parent - self.collector = collector - - # Settings for collector. - self.sample_rate_min = collector.tick_interval_min - self.sample_rate_max = collector.tick_interval_max - self.sample_rate = collector.tick_interval - self.dump_rate_min = collector.sample_buf_len_min - self.dump_rate_max = collector.sample_buf_len_max - self.dump_rate = collector.sample_buf_len - - # Settings for GUI. + self.conf_file = expanduser("~") + "/IcepapOSC/settings.ini" + if not exists(self.conf_file): + self._create_file() + self.sample_rate_min = 10 # [milliseconds] + self.sample_rate_max = 1000 # [milliseconds] + self.dump_rate_min = 1 + self.dump_rate_max = 100 self.default_x_axis_length_min = 5 # [Seconds] self.default_x_axis_length_max = 3600 # [Seconds] - self.default_x_axis_len = 30 # [Seconds] + self.sample_rate = 0 + self.dump_rate = 0 + self.default_x_axis_len = 0 + self.data_folder = '' + self._read_file() - # Settings for data storage. - self.data_folder = expanduser("~") # Home directory + def _create_file(self): + conf = SafeConfigParser() + conf.add_section('collector') + conf.add_section('gui') + conf.add_section('data') + conf.set('collector', 'tick_interval', '50') # [milliseconds] + conf.set('collector', 'sample_buf_len', '2') + conf.set('gui', 'default_x_axis_len', '30') # [Seconds] + conf.set('data', 'unused', expanduser("~")) + with open(self.conf_file, 'w') as f: + conf.write(f) - def announce_update(self): - self.collector.tick_interval = self.sample_rate - self.collector.sample_buf_len = self.dump_rate - self.parent.settings_updated() + def update(self): + """Called when a setting has been changed.""" + conf = SafeConfigParser() + conf.read(self.conf_file) + conf.set('collector', 'tick_interval', str(self.sample_rate)) + conf.set('collector', 'sample_buf_len', str(self.dump_rate)) + conf.set('gui', 'default_x_axis_len', str(self.default_x_axis_len)) + conf.set('data', 'unused', str(self.data_folder)) + with open(self.conf_file, 'w') as f: + conf.write(f) + + def _read_file(self): + conf = SafeConfigParser() + conf.read(self.conf_file) + self.sample_rate = conf.getint('collector', 'tick_interval') + self.dump_rate = conf.getint('collector', 'sample_buf_len') + self.default_x_axis_len = conf.getint('gui', 'default_x_axis_len') + self.data_folder = conf.get('data', 'unused') diff --git a/icepaposc/window_main.py b/icepaposc/window_main.py index d417f93..f7a996c 100644 --- a/icepaposc/window_main.py +++ b/icepaposc/window_main.py @@ -56,11 +56,13 @@ def __init__(self, host, port, timeout, siglist, selected_driver=None): self.setAttribute(Qt.WA_DeleteOnClose, True) self.ui.setupUi(self) self.setWindowTitle('Oscilloscope | ' + host) + self.settings = Settings() try: self.collector = Collector(host, port, timeout, + self.settings, self.callback_collect) except Exception as e: msg = 'Failed to create main window.\n{}'.format(e) @@ -71,7 +73,6 @@ def __init__(self, host, port, timeout, siglist, selected_driver=None): self.subscriptions = {} self.curve_items = [] self._paused = False - self.settings = Settings(self, self.collector) # Set up the plot area. self.plot_widget = pg.PlotWidget() From 3d79bfb25529b41910abfd8882c9c0b95de3fbfb Mon Sep 17 00:00:00 2001 From: Jarkko Inki Date: Thu, 20 Jun 2019 11:54:14 +0200 Subject: [PATCH 08/46] Reverted previous commit and added caption to file save dialog. --- icepaposc/window_main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/icepaposc/window_main.py b/icepaposc/window_main.py index d417f93..95e5ce7 100644 --- a/icepaposc/window_main.py +++ b/icepaposc/window_main.py @@ -443,12 +443,12 @@ def enable_action(self, enable=True): def _save_to_file(self): if not self.curve_items: return - extension = ".csv" - fn = QFileDialog.getSaveFileName(filter=extension) + capt = "Save to csv file" + fn = QFileDialog.getSaveFileName(caption=capt, filter="*.csv") if not fn: return - if fn[-4:] != extension: - fn = fn + extension + if fn[-4:] != ".csv": + fn = fn + ".csv" try: open(fn, "w+") except Exception as e: From 3c269bc40ecc17a9882340c11105553bd4967d36 Mon Sep 17 00:00:00 2001 From: Jarkko Inki Date: Thu, 20 Jun 2019 14:40:46 +0200 Subject: [PATCH 09/46] Removed dependency to pandas. --- icepaposc/window_main.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/icepaposc/window_main.py b/icepaposc/window_main.py index 95e5ce7..d6a87da 100644 --- a/icepaposc/window_main.py +++ b/icepaposc/window_main.py @@ -31,7 +31,6 @@ from curve_item import CurveItem import pyqtgraph as pg import numpy as np -import pandas as pd import collections import time @@ -450,15 +449,15 @@ def _save_to_file(self): if fn[-4:] != ".csv": fn = fn + ".csv" try: - open(fn, "w+") + f = open(fn, "w+") except Exception as e: msg = 'Failed to open/create file: {}\n{}'.format(fn, e) print(msg) QMessageBox.critical(None, 'File Open Failed', msg) return - self._create_csv_file(fn) + self._create_csv_file(f) - def _create_csv_file(self, file_name): + def _create_csv_file(self, csv_file): my_dict = collections.OrderedDict() for ci in self.curve_items: header = "time-{}-{}".format(ci.driver_addr, ci.signal_name) @@ -472,8 +471,14 @@ def _create_csv_file(self, file_name): for key in my_dict: delta = len(my_dict[key_longest]) - len(my_dict[key]) my_dict[key] = delta * [np.nan] + my_dict[key] - df = pd.DataFrame(data=my_dict) - df.to_csv(file_name) + for key in my_dict: + csv_file.write(",{}".format(key)) + csv_file.write("\n") + for idx in range(0, len(my_dict[key_longest])): + line = str(idx) + for key in my_dict: + line += ",{}".format(my_dict[key][idx]) + csv_file.write(line + '\n') def _display_settings_dlg(self): self.enable_action(False) From f2762a2cc14eb2bfda93faad477c60a6fa89308c Mon Sep 17 00:00:00 2001 From: Jarkko Inki Date: Thu, 20 Jun 2019 17:54:00 +0200 Subject: [PATCH 10/46] Added continuous saving of data. --- icepaposc/curve_item.py | 8 +++--- icepaposc/window_main.py | 59 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/icepaposc/curve_item.py b/icepaposc/curve_item.py index db874b2..07974c8 100644 --- a/icepaposc/curve_item.py +++ b/icepaposc/curve_item.py @@ -108,8 +108,8 @@ def create_curve(self): def update_curve(self, time_min, time_max): """Updates the curve with recent collected data.""" with self.lock: - idx_min = self._get_time_index(time_min) - idx_max = self._get_time_index(time_max) + idx_min = self.get_time_index(time_min) + idx_max = self.get_time_index(time_max) self.curve.setData(x=self.array_time[idx_min:idx_max], y=self.array_val[idx_min:idx_max]) @@ -159,14 +159,14 @@ def get_y(self, time_val): Return: Signal value corresponding to an adjacent sample in time. """ with self.lock: - idx = self._get_time_index(time_val) + idx = self.get_time_index(time_val) return self.array_val[idx] def clear(self): self.array_time[:] = [] self.array_val[:] = [] - def _get_time_index(self, time_val): + def get_time_index(self, time_val): """ Retrieve the sample index corresponding to the provided time value. diff --git a/icepaposc/window_main.py b/icepaposc/window_main.py index d6a87da..58b466e 100644 --- a/icepaposc/window_main.py +++ b/icepaposc/window_main.py @@ -23,6 +23,7 @@ from PyQt4.QtGui import QBoxLayout from PyQt4.QtGui import QColor from PyQt4.QtCore import Qt +from PyQt4.QtCore import QTimer from ui.ui_window_main import Ui_WindowMain from collector import Collector from dialog_settings import DialogSettings @@ -135,6 +136,15 @@ def __init__(self, host, port, timeout, siglist, selected_driver=None): return self._add_signal(int(lst[0]), lst[1], int(lst[2])) + # Set up continuous saving of collected signal data. + self.cont_file_path = self._get_cont_file_path() + self.cont_save_ticker = QTimer() + self.cont_save_ticker.timeout.connect(self._continuous_save) + self.cont_save_interval = 5 * 60 * 1000 # Fixed 5 minutes. + self.cont_save_time = time.time() + self.cont_save_ticker.start(self.cont_save_interval) + + def _fill_combo_box_driver_ids(self, selected_driver): driver_ids = self.collector.get_available_drivers() for driver_id in driver_ids: @@ -278,6 +288,7 @@ def _add_signal(self, driver_addr, signal_name, y_axis): self._update_button_status() def _remove_selected_signal(self): + self._continuous_save() index = self.ui.lvActiveSig.currentRow() ci = self.curve_items[index] self.collector.unsubscribe(ci.subscription_id) @@ -289,6 +300,7 @@ def _remove_selected_signal(self): def _remove_all_signals(self): """Removes all signals.""" + self._continuous_save() for ci in self.curve_items: self.collector.unsubscribe(ci.subscription_id) self._remove_curve_plot(ci) @@ -390,6 +402,7 @@ def _signals_target(self): def _clear_all(self): """Clear all the displayed curves.""" + self._continuous_save() for ci in self.curve_items: ci.clear() @@ -456,6 +469,7 @@ def _save_to_file(self): QMessageBox.critical(None, 'File Open Failed', msg) return self._create_csv_file(f) + f.close() def _create_csv_file(self, csv_file): my_dict = collections.OrderedDict() @@ -480,6 +494,51 @@ def _create_csv_file(self, csv_file): line += ",{}".format(my_dict[key][idx]) csv_file.write(line + '\n') + def _get_cont_file_path(self): + time_str = time.strftime("%Y%m%d_%H%M%S", time.localtime()) + file_name = "IcepapOSC_{}.csv".format(time_str) + return self.settings.data_folder + '/' + file_name + + def _continuous_save(self): + if not self.curve_items: + return + self.cont_save_ticker.stop() + new_save_time = time.time() + fn = self.cont_file_path + try: + f = open(fn, "w+") + except Exception as e: + msg = 'Failed to create file: {}\n{}'.format(fn, e) + print(msg) + QMessageBox.critical(None, 'File Create Failed', msg) + return + my_dict = collections.OrderedDict() + for ci in self.curve_items: + start_idx = ci.get_time_index(self.cont_save_time) + header = "time-{}-{}".format(ci.driver_addr, ci.signal_name) + my_dict[header] = ci.array_time[start_idx:] + header = "val-{}-{}".format(ci.driver_addr, ci.signal_name) + my_dict[header] = ci.array_val[start_idx:] + key_longest = my_dict.keys()[0] + for key in my_dict: + if my_dict[key][0] < my_dict[key_longest][0]: + key_longest = key + for key in my_dict: + delta = len(my_dict[key_longest]) - len(my_dict[key]) + my_dict[key] = delta * [np.nan] + my_dict[key] + for key in my_dict: + f.write(",{}".format(key)) + f.write("\n") + for idx in range(0, len(my_dict[key_longest])): + line = str(idx) + for key in my_dict: + line += ",{}".format(my_dict[key][idx]) + f.write(line + '\n') + f.close() + self.cont_file_path = self._get_cont_file_path() + self.cont_save_time = new_save_time + self.cont_save_ticker.start(self.cont_save_interval) + def _display_settings_dlg(self): self.enable_action(False) dlg = DialogSettings(self, self.settings) From 9b7a0d439f3e8942f9dbf0b5ce26fd6f6b6fd6cf Mon Sep 17 00:00:00 2001 From: Jarkko Inki Date: Thu, 20 Jun 2019 18:01:28 +0200 Subject: [PATCH 11/46] Removed an excess blank line. --- icepaposc/window_main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/icepaposc/window_main.py b/icepaposc/window_main.py index 58b466e..1eae150 100644 --- a/icepaposc/window_main.py +++ b/icepaposc/window_main.py @@ -144,7 +144,6 @@ def __init__(self, host, port, timeout, siglist, selected_driver=None): self.cont_save_time = time.time() self.cont_save_ticker.start(self.cont_save_interval) - def _fill_combo_box_driver_ids(self, selected_driver): driver_ids = self.collector.get_available_drivers() for driver_id in driver_ids: From 13fdbc40101573d54fade86982bba802329e09e3 Mon Sep 17 00:00:00 2001 From: Jarkko Inki Date: Mon, 24 Jun 2019 10:13:07 +0200 Subject: [PATCH 12/46] Added settings for auto save interval and auto save on/off --- icepaposc/dialog_settings.py | 35 +++++++++++++---- icepaposc/settings.py | 12 ++++-- icepaposc/ui/dialog_settings.ui | 60 +++++++++++++++++++++++++++--- icepaposc/ui/ui_dialog_settings.py | 51 +++++++++++++++++++------ icepaposc/window_main.py | 28 ++++++++++---- 5 files changed, 151 insertions(+), 35 deletions(-) diff --git a/icepaposc/dialog_settings.py b/icepaposc/dialog_settings.py index 53e3963..1b55685 100644 --- a/icepaposc/dialog_settings.py +++ b/icepaposc/dialog_settings.py @@ -43,16 +43,22 @@ def __init__(self, parent, settings): self.ui.sbDumpRate.setMinimum(self.settings.dump_rate_min) self.ui.sbDumpRate.setMaximum(self.settings.dump_rate_max) self.ui.sbDumpRate.setValue(self.settings.dump_rate) - self.ui.sbLenAxisX.setMinimum(self.settings.default_x_axis_length_min) - self.ui.sbLenAxisX.setMaximum(self.settings.default_x_axis_length_max) + self.ui.sbLenAxisX.setMinimum(self.settings.default_x_axis_len_min) + self.ui.sbLenAxisX.setMaximum(self.settings.default_x_axis_len_max) self.ui.sbLenAxisX.setValue(self.settings.default_x_axis_len) - self.ui.leDataFolder.setText(self.settings.data_folder) + self.ui.cbUseAutoSave.setEnabled(self.settings.as_enabled) + self.ui.sbAutoSaveInterval.setMinimum(self.settings.as_interval_min) + self.ui.sbAutoSaveInterval.setMaximum(self.settings.as_interval_max) + self.ui.sbAutoSaveInterval.setValue(self.settings.as_interval) + self.ui.leDataFolder.setText(self.settings.as_folder) self.apply_button.setDisabled(True) def _connect_signals(self): self.ui.sbSampleRate.valueChanged.connect(self._sample_rate_changed) self.ui.sbDumpRate.valueChanged.connect(self._dump_rate_changed) self.ui.sbLenAxisX.valueChanged.connect(self._x_axis_length_changed) + self.ui.cbUseAutoSave.stateChanged.connect(self._as_state_changed) + self.ui.sbAutoSaveInterval.valueChanged.connect(self._as_intvl_changed) self.ui.btnOpenFolderDlg.clicked.connect(self._launch_folder_dialog) self.ui.leDataFolder.textChanged.connect(self._set_apply_state) self.apply_button.clicked.connect(self._apply) @@ -73,26 +79,41 @@ def _set_apply_state(self): eq = self.ui.sbSampleRate.value() == self.settings.sample_rate and \ self.ui.sbDumpRate.value() == self.settings.dump_rate and \ self.ui.sbLenAxisX.value() == self.settings.default_x_axis_len and \ - self.ui.leDataFolder.text() == self.settings.data_folder + self.ui.cbUseAutoSave.isChecked() == self.settings.as_enabled and \ + self.ui.sbAutoSaveInterval.value() == \ + self.settings.as_interval and \ + self.ui.leDataFolder.text() == self.settings.as_folder self.apply_button.setDisabled(eq) def _update_gui_rate(self): update_rate = self.ui.sbSampleRate.value() * self.ui.sbDumpRate.value() self.ui.leGuiUpdateRate.setText(str(update_rate)) + def _as_state_changed(self): + use = self.ui.cbUseAutoSave.isChecked() + self.ui.sbAutoSaveInterval.setEnabled(use) + self.ui.leDataFolder.setEnabled(use) + self.ui.btnOpenFolderDlg.setEnabled(use) + self._set_apply_state() + + def _as_intvl_changed(self): + self._set_apply_state() + def _launch_folder_dialog(self): folder_name = QFileDialog.getExistingDirectory() self.ui.leDataFolder.setText(folder_name) self._set_apply_state() def _apply(self): - data_folder = self.ui.leDataFolder.text() - if not self._is_valid_folder(data_folder): + auto_save_folder = self.ui.leDataFolder.text() + if not self._is_valid_folder(auto_save_folder): return self.settings.sample_rate = self.ui.sbSampleRate.value() self.settings.dump_rate = self.ui.sbDumpRate.value() self.settings.default_x_axis_len = self.ui.sbLenAxisX.value() - self.settings.data_folder = data_folder + self.settings.as_enabled = self.ui.cbUseAutoSave.isChecked() + self.settings.as_interval = self.ui.sbAutoSaveInterval.value() + self.settings.as_folder = auto_save_folder self.settings.announce_update() self.apply_button.setDisabled(True) diff --git a/icepaposc/settings.py b/icepaposc/settings.py index f7fb826..4e2194a 100644 --- a/icepaposc/settings.py +++ b/icepaposc/settings.py @@ -38,12 +38,16 @@ def __init__(self, parent, collector): self.dump_rate = collector.sample_buf_len # Settings for GUI. - self.default_x_axis_length_min = 5 # [Seconds] - self.default_x_axis_length_max = 3600 # [Seconds] + self.default_x_axis_len_min = 5 # [Seconds] + self.default_x_axis_len_max = 3600 # [Seconds] self.default_x_axis_len = 30 # [Seconds] - # Settings for data storage. - self.data_folder = expanduser("~") # Home directory + # Settings for auto save. + self.as_enabled = True + self.as_interval_min = 1 # [Minutes] + self.as_interval_max = 24 * 60 # [Minutes] + self.as_interval = 5 # [Minutes] + self.as_folder = expanduser("~") + "/IcepapOSC" def announce_update(self): self.collector.tick_interval = self.sample_rate diff --git a/icepaposc/ui/dialog_settings.ui b/icepaposc/ui/dialog_settings.ui index fb0b0b7..852546d 100644 --- a/icepaposc/ui/dialog_settings.ui +++ b/icepaposc/ui/dialog_settings.ui @@ -7,7 +7,7 @@ 0 0 559 - 292 + 338 @@ -155,18 +155,25 @@ - + 0 - 86 + 0 - Folder for Saved Data (filename: IcePapOSC_<date>_<time>.osc) + Auto Save (filename: IcepapOSC_<date>_<time>.csv) - + + + + Folder: + + + + @@ -177,6 +184,16 @@ + + + + + + true + + + + @@ -198,6 +215,39 @@ + + + + + 0 + 0 + + + + + 80 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Interval minutes: + + + + + + + Enable: + + + diff --git a/icepaposc/ui/ui_dialog_settings.py b/icepaposc/ui/ui_dialog_settings.py index e7e27da..72d1feb 100644 --- a/icepaposc/ui/ui_dialog_settings.py +++ b/icepaposc/ui/ui_dialog_settings.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'dialog_settings.ui' # -# Created: Wed May 29 08:55:21 2019 +# Created: Mon Jun 24 10:10:01 2019 # by: PyQt4 UI code generator 4.10.1 # # WARNING! All changes made in this file will be lost! @@ -26,7 +26,7 @@ def _translate(context, text, disambig): class Ui_DialogSettings(object): def setupUi(self, DialogSettings): DialogSettings.setObjectName(_fromUtf8("DialogSettings")) - DialogSettings.resize(559, 292) + DialogSettings.resize(559, 338) self.verticalLayout_2 = QtGui.QVBoxLayout(DialogSettings) self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2")) self.hl1 = QtGui.QHBoxLayout() @@ -99,16 +99,24 @@ def setupUi(self, DialogSettings): self.verticalLayout_2.addLayout(self.hl1) self.hl2 = QtGui.QHBoxLayout() self.hl2.setObjectName(_fromUtf8("hl2")) - self.gbDataFolder = QtGui.QGroupBox(DialogSettings) - self.gbDataFolder.setMinimumSize(QtCore.QSize(0, 86)) - self.gbDataFolder.setObjectName(_fromUtf8("gbDataFolder")) - self.gridLayout = QtGui.QGridLayout(self.gbDataFolder) + self.gbAutoSave = QtGui.QGroupBox(DialogSettings) + self.gbAutoSave.setMinimumSize(QtCore.QSize(0, 0)) + self.gbAutoSave.setObjectName(_fromUtf8("gbAutoSave")) + self.gridLayout = QtGui.QGridLayout(self.gbAutoSave) self.gridLayout.setObjectName(_fromUtf8("gridLayout")) - self.leDataFolder = QtGui.QLineEdit(self.gbDataFolder) + self.labelAutoSaveFolder = QtGui.QLabel(self.gbAutoSave) + self.labelAutoSaveFolder.setObjectName(_fromUtf8("labelAutoSaveFolder")) + self.gridLayout.addWidget(self.labelAutoSaveFolder, 2, 0, 1, 1) + self.leDataFolder = QtGui.QLineEdit(self.gbAutoSave) self.leDataFolder.setMinimumSize(QtCore.QSize(200, 0)) self.leDataFolder.setObjectName(_fromUtf8("leDataFolder")) - self.gridLayout.addWidget(self.leDataFolder, 0, 0, 1, 1) - self.btnOpenFolderDlg = QtGui.QPushButton(self.gbDataFolder) + self.gridLayout.addWidget(self.leDataFolder, 2, 1, 1, 1) + self.cbUseAutoSave = QtGui.QCheckBox(self.gbAutoSave) + self.cbUseAutoSave.setText(_fromUtf8("")) + self.cbUseAutoSave.setChecked(True) + self.cbUseAutoSave.setObjectName(_fromUtf8("cbUseAutoSave")) + self.gridLayout.addWidget(self.cbUseAutoSave, 0, 1, 1, 1) + self.btnOpenFolderDlg = QtGui.QPushButton(self.gbAutoSave) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -117,8 +125,24 @@ def setupUi(self, DialogSettings): self.btnOpenFolderDlg.setMaximumSize(QtCore.QSize(32, 32)) self.btnOpenFolderDlg.setAutoDefault(False) self.btnOpenFolderDlg.setObjectName(_fromUtf8("btnOpenFolderDlg")) - self.gridLayout.addWidget(self.btnOpenFolderDlg, 0, 1, 1, 1) - self.hl2.addWidget(self.gbDataFolder) + self.gridLayout.addWidget(self.btnOpenFolderDlg, 2, 2, 1, 1) + self.sbAutoSaveInterval = QtGui.QSpinBox(self.gbAutoSave) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.sbAutoSaveInterval.sizePolicy().hasHeightForWidth()) + self.sbAutoSaveInterval.setSizePolicy(sizePolicy) + self.sbAutoSaveInterval.setMinimumSize(QtCore.QSize(80, 0)) + self.sbAutoSaveInterval.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.sbAutoSaveInterval.setObjectName(_fromUtf8("sbAutoSaveInterval")) + self.gridLayout.addWidget(self.sbAutoSaveInterval, 1, 1, 1, 1) + self.labelAutoSaveInterval = QtGui.QLabel(self.gbAutoSave) + self.labelAutoSaveInterval.setObjectName(_fromUtf8("labelAutoSaveInterval")) + self.gridLayout.addWidget(self.labelAutoSaveInterval, 1, 0, 1, 1) + self.labelUseAutoSave = QtGui.QLabel(self.gbAutoSave) + self.labelUseAutoSave.setObjectName(_fromUtf8("labelUseAutoSave")) + self.gridLayout.addWidget(self.labelUseAutoSave, 0, 0, 1, 1) + self.hl2.addWidget(self.gbAutoSave) self.verticalLayout_2.addLayout(self.hl2) self.bbApplyClose = QtGui.QDialogButtonBox(DialogSettings) self.bbApplyClose.setOrientation(QtCore.Qt.Horizontal) @@ -140,6 +164,9 @@ def retranslateUi(self, DialogSettings): self.labelDumpRate.setText(_translate("DialogSettings", "Dump Rate [samples/dump]", None)) self.gbXAxis.setTitle(_translate("DialogSettings", "X-axis", None)) self.labelXAxisLength.setText(_translate("DialogSettings", "Default Length [sec]", None)) - self.gbDataFolder.setTitle(_translate("DialogSettings", "Folder for Saved Data (filename: IcePapOSC__ diff --git a/icepaposc/ui/ui_dialog_settings.py b/icepaposc/ui/ui_dialog_settings.py index 72d1feb..64ac625 100644 --- a/icepaposc/ui/ui_dialog_settings.py +++ b/icepaposc/ui/ui_dialog_settings.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'dialog_settings.ui' # -# Created: Mon Jun 24 10:10:01 2019 +# Created: Mon Jun 24 10:31:08 2019 # by: PyQt4 UI code generator 4.10.1 # # WARNING! All changes made in this file will be lost! @@ -113,7 +113,6 @@ def setupUi(self, DialogSettings): self.gridLayout.addWidget(self.leDataFolder, 2, 1, 1, 1) self.cbUseAutoSave = QtGui.QCheckBox(self.gbAutoSave) self.cbUseAutoSave.setText(_fromUtf8("")) - self.cbUseAutoSave.setChecked(True) self.cbUseAutoSave.setObjectName(_fromUtf8("cbUseAutoSave")) self.gridLayout.addWidget(self.cbUseAutoSave, 0, 1, 1, 1) self.btnOpenFolderDlg = QtGui.QPushButton(self.gbAutoSave) diff --git a/icepaposc/window_main.py b/icepaposc/window_main.py index 4f7f014..6182f36 100644 --- a/icepaposc/window_main.py +++ b/icepaposc/window_main.py @@ -501,7 +501,7 @@ def _continuous_save(self): tick_interval = 60000 * self.settings.as_interval self.cont_save_ticker.stop() new_save_time = time.time() - if not self.settings.as_enabled or not self.curve_items: + if not self.settings.use_auto_save or not self.curve_items: self.cont_file_path = self._get_cont_file_path() self.cont_save_time = new_save_time self.cont_save_ticker.start(tick_interval) From b9c0dabec825f36fd2360ff3d60ab9533e9bcc5b Mon Sep 17 00:00:00 2001 From: Jarkko Inki Date: Mon, 24 Jun 2019 11:10:33 +0200 Subject: [PATCH 14/46] Minor UI change. --- icepaposc/ui/dialog_settings.ui | 2 +- icepaposc/ui/ui_dialog_settings.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/icepaposc/ui/dialog_settings.ui b/icepaposc/ui/dialog_settings.ui index f2e2377..57adab2 100644 --- a/icepaposc/ui/dialog_settings.ui +++ b/icepaposc/ui/dialog_settings.ui @@ -234,7 +234,7 @@ - Interval minutes: + Interval [minutes]: diff --git a/icepaposc/ui/ui_dialog_settings.py b/icepaposc/ui/ui_dialog_settings.py index 64ac625..6e877b0 100644 --- a/icepaposc/ui/ui_dialog_settings.py +++ b/icepaposc/ui/ui_dialog_settings.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'dialog_settings.ui' # -# Created: Mon Jun 24 10:31:08 2019 +# Created: Mon Jun 24 11:09:38 2019 # by: PyQt4 UI code generator 4.10.1 # # WARNING! All changes made in this file will be lost! @@ -166,6 +166,6 @@ def retranslateUi(self, DialogSettings): self.gbAutoSave.setTitle(_translate("DialogSettings", "Auto Save (filename: IcepapOSC__ @@ -30,6 +30,9 @@ GUI Update Rate [ms] + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + @@ -84,6 +87,9 @@ Sample Rate [ms] + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + @@ -110,6 +116,9 @@ Dump Rate [samples/dump] + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + @@ -126,6 +135,9 @@ Default Length [sec] + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + @@ -166,14 +178,24 @@ Auto Save (filename: IcepapOSC_<date>_<time>.csv) - + + + + + + + + - Folder: + Folder + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + @@ -190,7 +212,7 @@ - + @@ -212,7 +234,7 @@ - + @@ -231,17 +253,33 @@ + + + + Enable + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + - + - Interval [minutes]: + Use Single File + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + - Enable: + Interval [minutes] + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter diff --git a/icepaposc/ui/ui_dialog_settings.py b/icepaposc/ui/ui_dialog_settings.py index 6e877b0..9489ec3 100644 --- a/icepaposc/ui/ui_dialog_settings.py +++ b/icepaposc/ui/ui_dialog_settings.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'dialog_settings.ui' # -# Created: Mon Jun 24 11:09:38 2019 +# Created: Tue Jun 25 08:50:57 2019 # by: PyQt4 UI code generator 4.10.1 # # WARNING! All changes made in this file will be lost! @@ -26,7 +26,7 @@ def _translate(context, text, disambig): class Ui_DialogSettings(object): def setupUi(self, DialogSettings): DialogSettings.setObjectName(_fromUtf8("DialogSettings")) - DialogSettings.resize(559, 338) + DialogSettings.resize(559, 364) self.verticalLayout_2 = QtGui.QVBoxLayout(DialogSettings) self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2")) self.hl1 = QtGui.QHBoxLayout() @@ -37,6 +37,7 @@ def setupUi(self, DialogSettings): self.glDataCollection = QtGui.QGridLayout(self.gbSampling) self.glDataCollection.setObjectName(_fromUtf8("glDataCollection")) self.labelGuiRate = QtGui.QLabel(self.gbSampling) + self.labelGuiRate.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.labelGuiRate.setObjectName(_fromUtf8("labelGuiRate")) self.glDataCollection.addWidget(self.labelGuiRate, 2, 1, 1, 1) self.sbSampleRate = QtGui.QSpinBox(self.gbSampling) @@ -62,6 +63,7 @@ def setupUi(self, DialogSettings): self.leGuiUpdateRate.setObjectName(_fromUtf8("leGuiUpdateRate")) self.glDataCollection.addWidget(self.leGuiUpdateRate, 2, 2, 1, 1) self.labelSampleRate = QtGui.QLabel(self.gbSampling) + self.labelSampleRate.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.labelSampleRate.setObjectName(_fromUtf8("labelSampleRate")) self.glDataCollection.addWidget(self.labelSampleRate, 0, 1, 1, 1) self.sbDumpRate = QtGui.QSpinBox(self.gbSampling) @@ -75,6 +77,7 @@ def setupUi(self, DialogSettings): self.sbDumpRate.setObjectName(_fromUtf8("sbDumpRate")) self.glDataCollection.addWidget(self.sbDumpRate, 1, 2, 1, 1) self.labelDumpRate = QtGui.QLabel(self.gbSampling) + self.labelDumpRate.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.labelDumpRate.setObjectName(_fromUtf8("labelDumpRate")) self.glDataCollection.addWidget(self.labelDumpRate, 1, 1, 1, 1) self.hl1.addWidget(self.gbSampling) @@ -83,6 +86,7 @@ def setupUi(self, DialogSettings): self.glXAxis = QtGui.QGridLayout(self.gbXAxis) self.glXAxis.setObjectName(_fromUtf8("glXAxis")) self.labelXAxisLength = QtGui.QLabel(self.gbXAxis) + self.labelXAxisLength.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.labelXAxisLength.setObjectName(_fromUtf8("labelXAxisLength")) self.glXAxis.addWidget(self.labelXAxisLength, 0, 0, 1, 1) self.sbLenAxisX = QtGui.QSpinBox(self.gbXAxis) @@ -104,13 +108,18 @@ def setupUi(self, DialogSettings): self.gbAutoSave.setObjectName(_fromUtf8("gbAutoSave")) self.gridLayout = QtGui.QGridLayout(self.gbAutoSave) self.gridLayout.setObjectName(_fromUtf8("gridLayout")) + self.cbAppend = QtGui.QCheckBox(self.gbAutoSave) + self.cbAppend.setText(_fromUtf8("")) + self.cbAppend.setObjectName(_fromUtf8("cbAppend")) + self.gridLayout.addWidget(self.cbAppend, 1, 1, 1, 1) self.labelAutoSaveFolder = QtGui.QLabel(self.gbAutoSave) + self.labelAutoSaveFolder.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.labelAutoSaveFolder.setObjectName(_fromUtf8("labelAutoSaveFolder")) - self.gridLayout.addWidget(self.labelAutoSaveFolder, 2, 0, 1, 1) + self.gridLayout.addWidget(self.labelAutoSaveFolder, 3, 0, 1, 1) self.leDataFolder = QtGui.QLineEdit(self.gbAutoSave) self.leDataFolder.setMinimumSize(QtCore.QSize(200, 0)) self.leDataFolder.setObjectName(_fromUtf8("leDataFolder")) - self.gridLayout.addWidget(self.leDataFolder, 2, 1, 1, 1) + self.gridLayout.addWidget(self.leDataFolder, 3, 1, 1, 1) self.cbUseAutoSave = QtGui.QCheckBox(self.gbAutoSave) self.cbUseAutoSave.setText(_fromUtf8("")) self.cbUseAutoSave.setObjectName(_fromUtf8("cbUseAutoSave")) @@ -124,7 +133,7 @@ def setupUi(self, DialogSettings): self.btnOpenFolderDlg.setMaximumSize(QtCore.QSize(32, 32)) self.btnOpenFolderDlg.setAutoDefault(False) self.btnOpenFolderDlg.setObjectName(_fromUtf8("btnOpenFolderDlg")) - self.gridLayout.addWidget(self.btnOpenFolderDlg, 2, 2, 1, 1) + self.gridLayout.addWidget(self.btnOpenFolderDlg, 3, 2, 1, 1) self.sbAutoSaveInterval = QtGui.QSpinBox(self.gbAutoSave) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -134,13 +143,19 @@ def setupUi(self, DialogSettings): self.sbAutoSaveInterval.setMinimumSize(QtCore.QSize(80, 0)) self.sbAutoSaveInterval.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.sbAutoSaveInterval.setObjectName(_fromUtf8("sbAutoSaveInterval")) - self.gridLayout.addWidget(self.sbAutoSaveInterval, 1, 1, 1, 1) - self.labelAutoSaveInterval = QtGui.QLabel(self.gbAutoSave) - self.labelAutoSaveInterval.setObjectName(_fromUtf8("labelAutoSaveInterval")) - self.gridLayout.addWidget(self.labelAutoSaveInterval, 1, 0, 1, 1) + self.gridLayout.addWidget(self.sbAutoSaveInterval, 2, 1, 1, 1) self.labelUseAutoSave = QtGui.QLabel(self.gbAutoSave) + self.labelUseAutoSave.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.labelUseAutoSave.setObjectName(_fromUtf8("labelUseAutoSave")) self.gridLayout.addWidget(self.labelUseAutoSave, 0, 0, 1, 1) + self.labelAppend = QtGui.QLabel(self.gbAutoSave) + self.labelAppend.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.labelAppend.setObjectName(_fromUtf8("labelAppend")) + self.gridLayout.addWidget(self.labelAppend, 1, 0, 1, 1) + self.labelAutoSaveInterval = QtGui.QLabel(self.gbAutoSave) + self.labelAutoSaveInterval.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.labelAutoSaveInterval.setObjectName(_fromUtf8("labelAutoSaveInterval")) + self.gridLayout.addWidget(self.labelAutoSaveInterval, 2, 0, 1, 1) self.hl2.addWidget(self.gbAutoSave) self.verticalLayout_2.addLayout(self.hl2) self.bbApplyClose = QtGui.QDialogButtonBox(DialogSettings) @@ -164,8 +179,9 @@ def retranslateUi(self, DialogSettings): self.gbXAxis.setTitle(_translate("DialogSettings", "X-axis", None)) self.labelXAxisLength.setText(_translate("DialogSettings", "Default Length [sec]", None)) self.gbAutoSave.setTitle(_translate("DialogSettings", "Auto Save (filename: IcepapOSC__