diff --git a/sohstationviewer/view/channel_dialog.py b/sohstationviewer/view/channel_dialog.py new file mode 100755 index 0000000000000000000000000000000000000000..a0c1af288a9c40c9321c6caf3352bab26ce5e70e --- /dev/null +++ b/sohstationviewer/view/channel_dialog.py @@ -0,0 +1,88 @@ +""" +channel_dialog.py +GUI to add/edit/remove channels +""" + +from sohstationviewer.view.core.db_gui_superclass import Ui_DBInfoDialog +from sohstationviewer.database.proccessDB import executeDB + + +class ChannelDialog(Ui_DBInfoDialog): + def __init__(self, parent): + super().__init__( + parent, ['No.', 'Channel', 'Label', 'Param', + 'ConvertFactor', 'Unit', 'FixPoint'], + 'channel', 'channels', resize_content_columns=[0, 4, 5, 6], + need_data_type_choice=True, requested_columns={3: 'Param'}, + check_fk=False) + self.setWindowTitle("Edit/Add/Delete Channels") + + def update_data_table_widget_items(self): + param_rows = executeDB("SELECT param from parameters") + self.param_choices = [''] + sorted([d[0] for d in param_rows]) + super(ChannelDialog, self).update_data_table_widget_items() + + def clear_first_row(self): + """ + device with no channels yet, there will be empty channel left + """ + self.data_table_widget.cellWidget(0, 1).setText('') + self.data_table_widget.cellWidget(0, 2).setText('') + self.data_table_widget.cellWidget(0, 3).setCurrentIndex(-1) + self.data_table_widget.cellWidget(0, 4).setText('1') + self.data_table_widget.cellWidget(0, 5).setText('') + self.data_table_widget.cellWidget(0, 6).setValue(0) + + def add_row(self, row_idx, fk=False): + self.addWidget(None, row_idx, 0) # No. + self.addWidget(self.data_list, row_idx, 1, foreign_key=fk) # chanID + self.addWidget(self.data_list, row_idx, 2) # label + self.addWidget(self.data_list, row_idx, 3, choices=self.param_choices) + self.addWidget(self.data_list, row_idx, 4, + field_name='convertFactor') + self.addWidget(self.data_list, row_idx, 5) # unit + self.addWidget(self.data_list, row_idx, 6, + range=[0, 5]) # fixPoint + + def data_type_changed(self): + self.data_type = self.data_type_combo_box.currentText() + self.update_data_table_widget_items() + + def get_data_list(self): + channel_rows = executeDB( + f"SELECT channel, label, param, convertFactor, unit, fixPoint " + f"FROM Channels " + f"WHERE dataType='{self.data_type}'") + return [[d[0], d[1], d[2], d[3], + '' if d[4] is None else d[4], + d[5]] + for d in channel_rows] + + def get_row_inputs(self, row_idx): + return [ + self.data_table_widget.cellWidget(row_idx, 1).text().strip(), + self.data_table_widget.cellWidget(row_idx, 2).text().strip(), + self.data_table_widget.cellWidget(row_idx, 3).currentText().strip(), + float(self.data_table_widget.cellWidget(row_idx, 4).text()), + self.data_table_widget.cellWidget(row_idx, 5).text(), + self.data_table_widget.cellWidget(row_idx, 6).value() + ] + + def remove_row(self, remove_row_idx): + self.data_table_widget.remove_row(remove_row_idx) + for i in range(remove_row_idx, self.data_table_widget.rowCount()): + cell_widget = self.data_table_widget.cellWidget(i, 0) + cell_widget.setText(str(i)) + + def update_data(self, row, widget_idx, list_idx): + insertsql = (f"INSERT INTO Channels VALUES" + f"('{row[0]}', '{row[1]}', '{row[2]}'," + f" {row[3]}, '{row[4]}', {row[5]}, '{self.data_type}')") + updatesql = (f"UPDATE Channels SET channel='{row[0]}', " + f"label='{row[1]}', param='{row[2]}', " + f"convertFactor={row[3]}, unit='{row[4]}' " + f"fixPoint={row[5]} " + f"WHERE channel='%s'" + f" AND dataType='{self.data_type}'") + return super().update_data( + row, widget_idx, list_idx, insertsql, updatesql) diff --git a/sohstationviewer/view/channel_prefer_dialog.py b/sohstationviewer/view/channel_prefer_dialog.py new file mode 100755 index 0000000000000000000000000000000000000000..d3d5751fb67f80633d42358e99518512de98dc7c --- /dev/null +++ b/sohstationviewer/view/channel_prefer_dialog.py @@ -0,0 +1,299 @@ +from PySide2 import QtWidgets, QtCore + +from sohstationviewer.database.proccessDB import ( + executeDB, trunc_addDB, executeDB_dict) +from sohstationviewer.controller.processing import readChannels, detectDataType +from sohstationviewer.controller.util import displayTrackingInfo + +INSTRUCTION = """ +Place lists of channels to be read in the IDs field.\n +Select the radiobutton for the list to be used in plotting. +""" +TOTAL_ROW = 20 + +COL = {'sel': 0, 'name': 1, 'dataType': 2, 'IDs': 3, 'clr': 4} + + +class ChannelPreferDialog(QtWidgets.QWidget): + def __init__(self, parent, dir_names): + super(ChannelPreferDialog, self).__init__() + self.parent = parent + self.dir_names = dir_names + self.setWindowTitle("Channel Preferences") + self.setGeometry(100, 100, 1100, 800) + main_layout = QtWidgets.QVBoxLayout() + main_layout.setContentsMargins(7, 7, 7, 7) + self.setLayout(main_layout) + + main_layout.addWidget(QtWidgets.QLabel(INSTRUCTION)) + + self.create_id_table_widget() + main_layout.addWidget(self.id_table_widget, 1) + + button_layout = self.create_buttons_section() + main_layout.addLayout(button_layout) + + self.trackingInfoTextBrowser = QtWidgets.QTextBrowser(self) + self.trackingInfoTextBrowser.setFixedHeight(60) + main_layout.addWidget(self.trackingInfoTextBrowser) + self.changed = False + + def create_buttons_section(self): + h_layout = QtWidgets.QHBoxLayout() + self.add_db_chan_btn = QtWidgets.QPushButton( + self, text='Add DB Channels') + self.add_db_chan_btn.clicked.connect(self.add_db_channels) + h_layout.addWidget(self.add_db_chan_btn) + + self.scan_chan_btn = QtWidgets.QPushButton( + self, text='Scan Channels from Data Source') + self.scan_chan_btn.clicked.connect(self.scan_channels) + h_layout.addWidget(self.scan_chan_btn) + + self.save_btn = QtWidgets.QPushButton(self, text='Save') + self.save_btn.clicked.connect(self.save) + h_layout.addWidget(self.save_btn) + + self.save_add_main_btn = QtWidgets.QPushButton( + self, text='Save - Add to Main') + self.save_add_main_btn.clicked.connect(self.save_add_to_main_and_close) + h_layout.addWidget(self.save_add_main_btn) + + self.close_btn = QtWidgets.QPushButton(self, text='Cancel') + self.close_btn.clicked.connect(self.close) + h_layout.addWidget(self.close_btn) + return h_layout + + def create_id_table_widget(self): + self.id_table_widget = QtWidgets.QTableWidget(self) + # self.id_table_widget.verticalHeader().hide() + self.id_table_widget.setColumnCount(5) + col_headers = ['', 'Name', 'DataType', 'IDs', 'Clear'] + self.id_table_widget.setHorizontalHeaderLabels(col_headers) + header = self.id_table_widget.horizontalHeader() + header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents) + header.setSectionResizeMode(3, QtWidgets.QHeaderView.Stretch) + + self.id_table_widget.setRowCount(TOTAL_ROW) + self.avail_data_types = self.get_data_types() + for row_idx in range(TOTAL_ROW): + self.add_row(row_idx) + + self.curr_row = -1 + self.update_data_table_widget_items() + + @QtCore.Slot() + def add_row(self, row_idx): + curr_sel_radio_btn = QtWidgets.QRadioButton(self) + curr_sel_radio_btn.clicked.connect( + lambda checked: self.curr_sel_changed(row_idx)) + self.id_table_widget.setCellWidget( + row_idx, COL['sel'], curr_sel_radio_btn) + + name_line_edit = QtWidgets.QLineEdit(self) + name_line_edit.textChanged.connect(self.input_changed) + self.id_table_widget.setCellWidget( + row_idx, COL['name'], name_line_edit) + + data_type_combo_box = QtWidgets.QComboBox(self) + data_type_combo_box.currentIndexChanged.connect(self.input_changed) + data_type_combo_box.addItems(['Unknown'] + self.avail_data_types) + data_type_combo_box.setCurrentIndex(-1) + self.id_table_widget.setCellWidget( + row_idx, COL['dataType'], data_type_combo_box) + + id_line_edit = QtWidgets.QLineEdit(self) + id_line_edit.textChanged.connect(self.input_changed) + self.id_table_widget.setCellWidget( + row_idx, COL['IDs'], id_line_edit) + + del_button = QtWidgets.QPushButton(self, text='CLR') + del_button.clicked.connect(lambda arg: self.clear_id(row_idx)) + self.id_table_widget.setCellWidget( + row_idx, COL['clr'], del_button) + + @QtCore.Slot() + def input_changed(self): + self.changed = True + + def update_data_table_widget_items(self): + id_rows = self.get_id_rows() + # first row + self.id_table_widget.cellWidget(0, COL['sel']).setChecked(True) + self.curr_sel_changed(0) + count = 0 + for r in id_rows: + self.id_table_widget.cellWidget( + count, COL['sel']).setChecked( + True if ['current'] == 1 else False) + self.id_table_widget.cellWidget( + count, COL['name']).setText(r['name']) + self.id_table_widget.cellWidget( + count, COL['dataType']).setCurrentText(r['dataType']) + self.id_table_widget.cellWidget( + count, COL['IDs']).setText(r['IDs']) + self.id_table_widget.cellWidget( + count, COL['sel']).setChecked( + True if r['current'] == 1 else False) + if ['current'] == 1: + self.curr_sel_changed(count) + count += 1 + self.update() + + def get_row(self, row_idx): + name_widget = self.id_table_widget.cellWidget(row_idx, COL['name']) + data_type_widget = self.id_table_widget.cellWidget(row_idx, COL['dataType']) + id_widget = self.id_table_widget.cellWidget(row_idx, COL['IDs']) + clear_widget = self.id_table_widget.cellWidget(row_idx, COL['clr']) + return name_widget, data_type_widget, id_widget, clear_widget + + @QtCore.Slot() + def curr_sel_changed(self, row_idx): + self.curr_row = row_idx + (self.name_widget, self.data_type_widget, + self.id_widget, self.clear_widget) = self.get_row(row_idx) + self.input_changed() + + @QtCore.Slot() + def clear_id(self, row_idx): + (name_widget, data_type_widget, id_widget, clear_widget) = self.get_row(row_idx) + if id_widget.text().strip() != "": + msg = ("Are you sure you want to delete the channel IDs of " + "row #%s?" % (row_idx + 1)) + result = QtWidgets.QMessageBox.question( + self, "Confirmation", msg, + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) + if result == QtWidgets.QMessageBox.No: + return + self.changed = True + if name_widget.text() != '': + self.changed = True + name_widget.setText('') + if data_type_widget.currentText != '': + self.changed = True + data_type_widget.setCurrentIndex(-1) + if id_widget.text() != '': + self.changed = True + id_widget.setText('') + + def validate_row(self, check_data_type=False): + if self.curr_row == -1: + msg = "Please select a row." + QtWidgets.QMessageBox.information(self, "Select row", msg) + return False + + if check_data_type: + self.data_type = self.data_type_widget.currentText() + if self.data_type not in self.avail_data_types: + msg = ("Please select a data type that isn't 'Unknown' for " + "the selected row.") + QtWidgets.QMessageBox.information( + self, "Select data type", msg) + return False + # # check IDs + # if self.id_widget.text().strip() != '': + # msg = ("The selected row's IDs will be overwritten.\n" + # "Do you want to continue?") + # result = QtWidgets.QMessageBox.question( + # self, "Confirmation", msg, + # QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) + # if result == QtWidgets.QMessageBox.No: + # return False + return True + + @QtCore.Slot() + def add_db_channels(self): + if not self.validate_row(check_data_type=True): + return + + db_channels = self.get_db_channels(self.data_type) + self.id_widget.setText(','.join(db_channels)) + + @QtCore.Slot() + def scan_channels(self): + if not self.validate_row(): + return + + data_type = detectDataType(self, self.dir_names) + if data_type in self.avail_data_types: + self.data_type_widget.setCurrentText(data_type) + else: + self.data_type_widget.setCurrenText('Unknown') + scanned_channels = readChannels(self, self.dir_names) + self.id_widget.setText(','.join(scanned_channels)) + + @QtCore.Slot() + def save(self): + if not self.validate_row(): + return + if self.changed: + msg = ("All IDs in the database will be overwritten with " + "current IDs in the dialog.\nClick Cancel to stop updating " + "database.") + result = QtWidgets.QMessageBox.question( + self, "Confirmation", msg, + QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) + if result == QtWidgets.QMessageBox.Cancel: + return False + sql_list = [] + for row_idx in range(TOTAL_ROW): + sql = self.save_row_sql(row_idx) + if sql is not None: + sql_list.append(sql) + if len(sql_list) == 0: + self.parent.IDs = [] + self.parent.IDsName = '' + self.parent.data_type = 'Unknown' + return True + + ret = trunc_addDB('ChannelPrefer', sql_list) + if ret is not True: + displayTrackingInfo(self.parent, ret, "error") + self.parent.IDs = [ + t.strip() for t in self.id_widget.text().split(',')] + self.parent.IDsName = self.name_widget.text().strip() + self.parent.IDsDataType = self.data_type_widget.currentText() + return True + + def save_row_sql(self, row_idx): + current = 1 if self.id_table_widget.cellWidget( + row_idx, COL['sel']).isChecked() else 0 + name = self.id_table_widget.cellWidget( + row_idx, COL['name']).text() + data_type = self.id_table_widget.cellWidget( + row_idx, COL['dataType']).currentText() + id = self.id_table_widget.cellWidget( + row_idx, COL['IDs']).text() + if id.strip() == '': + return + if name.strip() == '' and id.strip() != '': + msg = f"Please add Name for row {row_idx}." + QtWidgets.QMessageBox.information(self, "Missing info", msg) + return + return(f"INSERT INTO ChannelPrefer (name, IDs, dataType, current)" + f"VALUES ('{name}', '{id}', '{data_type}', {current})") + + @QtCore.Slot() + def save_add_to_main_and_close(self): + if not self.save(): + return + self.parent.currIDsNameLineEdit.setText(self.parent.IDsName) + self.parent.allChanCheckBox.setChecked(False) + self.close() + + def get_data_types(self): + data_type_rows = executeDB( + 'SELECT * FROM DataTypes ORDER BY dataType ASC') + return [d[0] for d in data_type_rows] + + def get_db_channels(self, data_type): + channel_rows = executeDB( + f"SELECT channel FROM CHANNELS WHERE dataType='{data_type}' " + f" ORDER BY dataType ASC") + return [c[0] for c in channel_rows] + + def get_id_rows(self): + i_ds_rows = executeDB_dict( + "SELECT name, IDs, dataType, current FROM ChannelPrefer " + " ORDER BY name ASC") + return i_ds_rows diff --git a/sohstationviewer/view/channeldialog.py b/sohstationviewer/view/channeldialog.py deleted file mode 100755 index 205f701d65d7738a4c7f4467a024b9c96db360d3..0000000000000000000000000000000000000000 --- a/sohstationviewer/view/channeldialog.py +++ /dev/null @@ -1,88 +0,0 @@ -""" -channeldialog.py -GUI to add/edit/remove channels -""" - -from sohstationviewer.view.core.dbgui_superclass import Ui_DBInfoDialog -from sohstationviewer.database.proccessDB import executeDB - - -class ChannelDialog(Ui_DBInfoDialog): - def __init__(parent, self): - super().__init__( - parent, ['No.', 'Channel', 'Label', 'Param', - 'ConvertFactor', 'Unit', 'FixPoint'], - 'channel', 'channels', resizeContentColumns=[0, 4, 5, 6], - needDataTypeChoice=True, requestedColumns={3: 'Param'}, - checkFK=False) - self.setWindowTitle("Edit/Add/Delete Channels") - - def updateDataTableWidgetItems(self): - paraRows = executeDB("SELECT param from parameters") - self.paramChoices = [''] + sorted([d[0] for d in paraRows]) - super(ChannelDialog, self).updateDataTableWidgetItems() - - def clearFirstRow(self): - """ - device with no channels yet, there will be empty channel left - """ - self.dataTableWidget.cellWidget(0, 1).setText('') - self.dataTableWidget.cellWidget(0, 2).setText('') - self.dataTableWidget.cellWidget(0, 3).setCurrentIndex(-1) - self.dataTableWidget.cellWidget(0, 4).setText('1') - self.dataTableWidget.cellWidget(0, 5).setText('') - self.dataTableWidget.cellWidget(0, 6).setValue(0) - - def addRow(self, rowidx, fk=False): - self.addWidget(None, rowidx, 0) # No. - self.addWidget(self.dataList, rowidx, 1, foreignkey=fk) # chanID - self.addWidget(self.dataList, rowidx, 2) # label - self.addWidget(self.dataList, rowidx, 3, choices=self.paramChoices) - self.addWidget(self.dataList, rowidx, 4, - fieldName='convertFactor') - self.addWidget(self.dataList, rowidx, 5) # unit - self.addWidget(self.dataList, rowidx, 6, - range=[0, 5]) # fixPoint - - def dataTypeChanged(self): - self.dataType = self.dataTypeCombobox.currentText() - self.updateDataTableWidgetItems() - - def getDataList(self): - channelRows = executeDB( - f"SELECT channel, label, param, convertFactor, unit, fixPoint " - f"FROM Channels " - f"WHERE dataType='{self.dataType}'") - return [[d[0], d[1], d[2], d[3], - '' if d[4] is None else d[4], - d[5]] - for d in channelRows] - - def getRowInputs(self, rowidx): - return [ - self.dataTableWidget.cellWidget(rowidx, 1).text().strip(), - self.dataTableWidget.cellWidget(rowidx, 2).text().strip(), - self.dataTableWidget.cellWidget(rowidx, 3).currentText().strip(), - float(self.dataTableWidget.cellWidget(rowidx, 4).text()), - self.dataTableWidget.cellWidget(rowidx, 5).text(), - self.dataTableWidget.cellWidget(rowidx, 6).value() - ] - - def removeRow(self, removeRowidx): - self.dataTableWidget.removeRow(removeRowidx) - for i in range(removeRowidx, self.dataTableWidget.rowCount()): - cellWget = self.dataTableWidget.cellWidget(i, 0) - cellWget.setText(str(i)) - - def updateData(self, row, widgetidx, listidx): - insertsql = (f"INSERT INTO Channels VALUES" - f"('{row[0]}', '{row[1]}', '{row[2]}'," - f" {row[3]}, '{row[4]}', {row[5]}, '{self.dataType}')") - updatesql = (f"UPDATE Channels SET channel='{row[0]}', " - f"label='{row[1]}', param='{row[2]}', " - f"convertFactor={row[3]}, unit='{row[4]}' " - f"fixPoint={row[5]} " - f"WHERE channel='%s'" - f" AND dataType='{self.dataType}'") - return super().updateData( - row, widgetidx, listidx, insertsql, updatesql) diff --git a/sohstationviewer/view/channelpreferdialog.py b/sohstationviewer/view/channelpreferdialog.py deleted file mode 100755 index 25e62dd9a1118feafaaadb6f466de6de3ddff0c9..0000000000000000000000000000000000000000 --- a/sohstationviewer/view/channelpreferdialog.py +++ /dev/null @@ -1,299 +0,0 @@ -from PySide2 import QtWidgets, QtCore - -from sohstationviewer.database.proccessDB import ( - executeDB, trunc_addDB, executeDB_dict) -from sohstationviewer.controller.processing import readChannels, detectDataType -from sohstationviewer.controller.util import displayTrackingInfo - -INSTRUCTION = """ -Place lists of channels to be read in the IDs field.\n -Select the radiobutton for the list to be used in plotting. -""" -TOTAL_ROW = 20 - -COL = {'sel': 0, 'name': 1, 'dataType': 2, 'IDs': 3, 'clr': 4} - - -class ChannelPreferDialog(QtWidgets.QWidget): - def __init__(self, parent, dirnames): - super(ChannelPreferDialog, self).__init__() - self.parent = parent - self.dirnames = dirnames - self.setWindowTitle("Channel Preferences") - self.setGeometry(100, 100, 1100, 800) - mainLayout = QtWidgets.QVBoxLayout() - mainLayout.setContentsMargins(7, 7, 7, 7) - self.setLayout(mainLayout) - - mainLayout.addWidget(QtWidgets.QLabel(INSTRUCTION)) - - self.createIDsTableWidget() - mainLayout.addWidget(self.IDsTableWidget, 1) - - buttonLayout = self.createButtonsSection() - mainLayout.addLayout(buttonLayout) - - self.trackingInfoTextBrowser = QtWidgets.QTextBrowser(self) - self.trackingInfoTextBrowser.setFixedHeight(60) - mainLayout.addWidget(self.trackingInfoTextBrowser) - self.changed = False - - def createButtonsSection(self): - hLayout = QtWidgets.QHBoxLayout() - self.addDBChanBtn = QtWidgets.QPushButton( - self, text='Add DB Channels') - self.addDBChanBtn.clicked.connect(self.addDBChannels) - hLayout.addWidget(self.addDBChanBtn) - - self.scanChanBtn = QtWidgets.QPushButton( - self, text='Scan Channels from Data Source') - self.scanChanBtn.clicked.connect(self.scanChannels) - hLayout.addWidget(self.scanChanBtn) - - self.saveBtn = QtWidgets.QPushButton(self, text='Save') - self.saveBtn.clicked.connect(self.save) - hLayout.addWidget(self.saveBtn) - - self.save_addMainBtn = QtWidgets.QPushButton( - self, text='Save - Add to Main') - self.save_addMainBtn.clicked.connect(self.save_addToMainNClose) - hLayout.addWidget(self.save_addMainBtn) - - self.closeBtn = QtWidgets.QPushButton(self, text='Cancel') - self.closeBtn.clicked.connect(self.close) - hLayout.addWidget(self.closeBtn) - return hLayout - - def createIDsTableWidget(self): - self.IDsTableWidget = QtWidgets.QTableWidget(self) - # self.IDsTableWidget.verticalHeader().hide() - self.IDsTableWidget.setColumnCount(5) - colHeaders = ['', 'Name', 'DataType', 'IDs', 'Clear'] - self.IDsTableWidget.setHorizontalHeaderLabels(colHeaders) - header = self.IDsTableWidget.horizontalHeader() - header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents) - header.setSectionResizeMode(3, QtWidgets.QHeaderView.Stretch) - - self.IDsTableWidget.setRowCount(TOTAL_ROW) - self.availDataTypes = self.getDataTypes() - for rowidx in range(TOTAL_ROW): - self.addRow(rowidx) - - self.currRow = -1 - self.updateDataTableWidgetItems() - - @QtCore.Slot() - def addRow(self, rowidx): - currSelRadioBtn = QtWidgets.QRadioButton(self) - currSelRadioBtn.clicked.connect( - lambda checked: self.currSelChanged(rowidx)) - self.IDsTableWidget.setCellWidget( - rowidx, COL['sel'], currSelRadioBtn) - - nameLineEdit = QtWidgets.QLineEdit(self) - nameLineEdit.textChanged.connect(self.inputChanged) - self.IDsTableWidget.setCellWidget( - rowidx, COL['name'], nameLineEdit) - - dataTypeCombobox = QtWidgets.QComboBox(self) - dataTypeCombobox.currentIndexChanged.connect(self.inputChanged) - dataTypeCombobox.addItems(['Unknown'] + self.availDataTypes) - dataTypeCombobox.setCurrentIndex(-1) - self.IDsTableWidget.setCellWidget( - rowidx, COL['dataType'], dataTypeCombobox) - - IDsLineEdit = QtWidgets.QLineEdit(self) - IDsLineEdit.textChanged.connect(self.inputChanged) - self.IDsTableWidget.setCellWidget( - rowidx, COL['IDs'], IDsLineEdit) - - delButton = QtWidgets.QPushButton(self, text='CLR') - delButton.clicked.connect(lambda arg: self.clearIDs(rowidx)) - self.IDsTableWidget.setCellWidget( - rowidx, COL['clr'], delButton) - - @QtCore.Slot() - def inputChanged(self): - self.changed = True - - def updateDataTableWidgetItems(self): - IDsRows = self.getIDsRows() - # first row - self.IDsTableWidget.cellWidget(0, COL['sel']).setChecked(True) - self.currSelChanged(0) - count = 0 - for r in IDsRows: - self.IDsTableWidget.cellWidget( - count, COL['sel']).setChecked( - True if ['current'] == 1 else False) - self.IDsTableWidget.cellWidget( - count, COL['name']).setText(r['name']) - self.IDsTableWidget.cellWidget( - count, COL['dataType']).setCurrentText(r['dataType']) - self.IDsTableWidget.cellWidget( - count, COL['IDs']).setText(r['IDs']) - self.IDsTableWidget.cellWidget( - count, COL['sel']).setChecked( - True if r['current'] == 1 else False) - if ['current'] == 1: - self.currSelChanged(count) - count += 1 - self.update() - - def getRow(self, rowidx): - nameWget = self.IDsTableWidget.cellWidget(rowidx, COL['name']) - dataTypeWget = self.IDsTableWidget.cellWidget(rowidx, COL['dataType']) - IDsWget = self.IDsTableWidget.cellWidget(rowidx, COL['IDs']) - clearWget = self.IDsTableWidget.cellWidget(rowidx, COL['clr']) - return (nameWget, dataTypeWget, IDsWget, clearWget) - - @QtCore.Slot() - def currSelChanged(self, rowidx): - self.currRow = rowidx - (self.nameWget, self.dataTypeWget, - self.IDsWget, self.clearWget) = self.getRow(rowidx) - self.inputChanged() - - @QtCore.Slot() - def clearIDs(self, rowidx): - (nameWget, dataTypeWget, IDsWget, clearWget) = self.getRow(rowidx) - if IDsWget.text().strip() != "": - msg = ("Are you sure you want to delete the channel IDs of " - "row #%s?" % (rowidx+1)) - result = QtWidgets.QMessageBox.question( - self, "Confirmation", msg, - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) - if result == QtWidgets.QMessageBox.No: - return - self.changed = True - if nameWget.text() != '': - self.changed = True - nameWget.setText('') - if dataTypeWget.currentText != '': - self.changed = True - dataTypeWget.setCurrentIndex(-1) - if IDsWget.text() != '': - self.changed = True - IDsWget.setText('') - - def validateRow(self, checkDataType=False): - if self.currRow == -1: - msg = ("Please select a row.") - QtWidgets.QMessageBox.information(self, "Select row", msg) - return False - - if checkDataType: - self.dataType = self.dataTypeWget.currentText() - if self.dataType not in self.availDataTypes: - msg = ("Please select a data type that isn't 'Unknown' for " - "the selected row.") - QtWidgets.QMessageBox.information( - self, "Select data type", msg) - return False - # # check IDs - # if self.IDsWget.text().strip() != '': - # msg = ("The selected row's IDs will be overwritten.\n" - # "Do you want to continue?") - # result = QtWidgets.QMessageBox.question( - # self, "Confirmation", msg, - # QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) - # if result == QtWidgets.QMessageBox.No: - # return False - return True - - @QtCore.Slot() - def addDBChannels(self): - if not self.validateRow(checkDataType=True): - return - - dbChannels = self.getDBChannels(self.dataType) - self.IDsWget.setText(','.join(dbChannels)) - - @QtCore.Slot() - def scanChannels(self): - if not self.validateRow(): - return - - dataType = detectDataType(self, self.dirnames) - if dataType in self.availDataTypes: - self.dataTypeWget.setCurrentText(dataType) - else: - self.dataTypeWget.setCurrenText('Unknown') - scannedChannels = readChannels(self, self.dirnames) - self.IDsWget.setText(','.join(scannedChannels)) - - @QtCore.Slot() - def save(self): - if not self.validateRow(): - return - if self.changed: - msg = ("All IDs in the database will be overwritten with " - "current IDs in the dialog.\nClick Cancel to stop updating " - "database.") - result = QtWidgets.QMessageBox.question( - self, "Confirmation", msg, - QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) - if result == QtWidgets.QMessageBox.Cancel: - return False - sqlList = [] - for rowidx in range(TOTAL_ROW): - sql = self.saveRowSql(rowidx) - if sql is not None: - sqlList.append(sql) - if len(sqlList) == 0: - self.parent.IDs = [] - self.parent.IDsName = '' - self.parent.dataType = 'Unknown' - return True - - ret = trunc_addDB('ChannelPrefer', sqlList) - if ret is not True: - displayTrackingInfo(self.parent, ret, "error") - self.parent.IDs = [ - t.strip() for t in self.IDsWget.text().split(',')] - self.parent.IDsName = self.nameWget.text().strip() - self.parent.IDsDataType = self.dataTypeWget.currentText() - return True - - def saveRowSql(self, rowidx): - current = 1 if self.IDsTableWidget.cellWidget( - rowidx, COL['sel']).isChecked() else 0 - name = self.IDsTableWidget.cellWidget( - rowidx, COL['name']).text() - dataType = self.IDsTableWidget.cellWidget( - rowidx, COL['dataType']).currentText() - IDs = self.IDsTableWidget.cellWidget( - rowidx, COL['IDs']).text() - if IDs.strip() == '': - return - if name.strip() == '' and IDs.strip() != '': - msg = f"Please add Name for row {rowidx}." - QtWidgets.QMessageBox.information(self, "Missing info", msg) - return - return (f"INSERT INTO ChannelPrefer (name, IDs, dataType, current)" - f"VALUES ('{name}', '{IDs}', '{dataType}', {current})") - - @QtCore.Slot() - def save_addToMainNClose(self): - if not self.save(): - return - self.parent.currIDsNameLineEdit.setText(self.parent.IDsName) - self.parent.allChanCheckBox.setChecked(False) - self.close() - - def getDataTypes(self): - dataTypeRows = executeDB( - 'SELECT * FROM DataTypes ORDER BY dataType ASC') - return [d[0] for d in dataTypeRows] - - def getDBChannels(self, dataType): - channelRows = executeDB( - f"SELECT channel FROM CHANNELS WHERE dataType='{dataType}' " - f" ORDER BY dataType ASC") - return [c[0] for c in channelRows] - - def getIDsRows(self): - IDsRows = executeDB_dict( - "SELECT name, IDs, dataType, current FROM ChannelPrefer " - " ORDER BY name ASC") - return IDsRows diff --git a/sohstationviewer/view/core/db_gui_superclass.py b/sohstationviewer/view/core/db_gui_superclass.py new file mode 100755 index 0000000000000000000000000000000000000000..4f5ca8bf84e06319f95d49195ba821d5fe6dde86 --- /dev/null +++ b/sohstationviewer/view/core/db_gui_superclass.py @@ -0,0 +1,368 @@ +from PySide2 import QtWidgets, QtGui + +from sohstationviewer.database.proccessDB import executeDB + + +def set_widget_color(widget, changed=False, read_only=False): + palette = QtGui.QPalette() + + if read_only: + # grey text + palette.setColor(QtGui.QPalette.Text, QtGui.QColor(100, 100, 100)) + # light blue background + palette.setColor(QtGui.QPalette.Base, QtGui.QColor(210, 240, 255)) + widget.setReadOnly(True) + widget.setPalette(palette) + return + else: + # white background + palette.setColor(QtGui.QPalette.Base, QtGui.QColor(255, 255, 255)) + if changed: + # red text + palette.setColor(QtGui.QPalette.Text, QtGui.QColor(255, 0, 0)) + else: + try: + if widget.isReadOnly(): + # grey text + palette.setColor( + QtGui.QPalette.Text, QtGui.QColor(100, 100, 100)) + else: + # black text + palette.setColor(QtGui.QPalette.Text, QtGui.QColor(0, 0, 0)) + except AttributeError: + palette.setColor(QtGui.QPalette.Text, QtGui.QColor(0, 0, 0)) + widget.setPalette(palette) + + +class Ui_DBInfoDialog(QtWidgets.QWidget): + def __init__(self, parent, column_headers, col_name, table_name, + resize_content_columns=[], requested_columns={}, + need_data_type_choice=False, check_fk=True): + self.total_col = len(column_headers) + self.column_headers = column_headers + self.resize_content_columns = resize_content_columns + self.requested_columns = requested_columns + self.need_data_type_choice = need_data_type_choice + self.col_name = col_name + self.table_name = table_name + self.check_fk = check_fk + super(Ui_DBInfoDialog, self).__init__() + + self.setGeometry(100, 100, 900, 900) + main_layout = QtWidgets.QVBoxLayout() + self.setLayout(main_layout) + + button_layout = self.create_buttons_section() + main_layout.addLayout(button_layout) + + self.create_data_table_widget() + main_layout.addWidget(self.data_table_widget, 1) + if self.table_name != '': + instruction = ("Background: LIGHT BLUE - Non Editable due to " + "FK constrain; " + "WHITE - Editable. " + "Text: BLACK - Saved; RED - Not saved") + # TODO: add question mark button to give instruction + # if self.table_name == 'parameters': + # instruction += ( + # "\nValueColors is requested for multiColorDots plotType." + # "Value from low to high" + # "\nThe format for less than or equal value pair is: " + # "value:color" + # "\nThe format for greater than value pair is: " + # "+value:color with " + # "color=R,Y,G,M,C.\n Ex: 2.3:C|+4:M") + + main_layout.addWidget(QtWidgets.QLabel(instruction)) + + def addWidget(self, data_list, row_idx, col_idx, foreign_key=False, + choices=None, range=None, field_name=''): + if data_list is None: + text = str(row_idx) # row number + else: + text = (str(data_list[row_idx][col_idx - 1]) + if row_idx < len(data_list) else '') + + if data_list is None: + widget = QtWidgets.QPushButton(text) + elif choices is None and range is None: + widget = QtWidgets.QLineEdit(text) + if field_name == 'convertFactor': + # precision=6 + validator = QtGui.QDoubleValidator(0.0, 5.0, 6) + validator.setNotation(QtGui.QDoubleValidator.StandardNotation) + widget.setValidator(validator) + if text == '': + widget.setText('1') + elif choices: + widget = QtWidgets.QComboBox() + widget.addItems(choices) + widget.setCurrentText(text) + elif range: + widget = QtWidgets.QSpinBox() + widget.setMinimum(range[0]) + widget.setMaximum(range[1]) + if text in ["", None, 'None']: + widget.setValue(range[0]) + else: + widget.setValue(int(text)) + + if data_list is None: + set_widget_color(widget) + widget.setFixedWidth(40) + widget.clicked.connect( + lambda: self.row_number_clicked(widget)) + elif foreign_key: + set_widget_color(widget, read_only=True) + else: + new = False if row_idx < len(data_list) else True + set_widget_color(widget, read_only=False, changed=new) + if choices is None and range is None: + widget.textChanged.connect( + lambda changed_text: + self.cell_input_change(changed_text, row_idx, col_idx)) + elif choices: + widget.currentTextChanged.connect( + lambda changed_text: + self.cell_input_change(changed_text, row_idx, col_idx)) + elif range: + widget.valueChanged.connect( + lambda changed_text: + self.cell_input_change(changed_text, row_idx, col_idx)) + self.data_table_widget.setCellWidget(row_idx, col_idx, widget) + + def row_number_clicked(self, widget): + self.data_table_widget.selectRow(int(widget.text())) + self.data_table_widget.repaint() + + def create_buttons_section(self): + h_layout = QtWidgets.QHBoxLayout() + + if self.need_data_type_choice: + self.data_type_combo_box = QtWidgets.QComboBox(self) + data_type_rows = executeDB('SELECT * FROM DataTypes') + self.data_type_combo_box.addItems([d[0] for d in data_type_rows]) + self.data_type_combo_box.currentTextChanged.connect( + self.data_type_changed) + h_layout.addWidget(self.data_type_combo_box) + if self.table_name != '': + self.add_row_btn = QtWidgets.QPushButton(self, text='ADD ROW') + self.add_row_btn.setFixedWidth(300) + self.add_row_btn.clicked.connect(self.add_new_row) + h_layout.addWidget(self.add_row_btn) + + self.save_changes_btn = QtWidgets.QPushButton( + self, text='SAVE CHANGES') + self.save_changes_btn.setFixedWidth(300) + self.save_changes_btn.clicked.connect(self.save_changes) + h_layout.addWidget(self.save_changes_btn) + + self.close_btn = QtWidgets.QPushButton(self, text='CLOSE') + self.close_btn.setFixedWidth(300) + self.close_btn.clicked.connect(self.close) + h_layout.addWidget(self.close_btn) + return h_layout + + def create_data_table_widget(self): + self.data_table_widget = QtWidgets.QTableWidget(self) + self.data_table_widget.verticalHeader().hide() + self.data_table_widget.setColumnCount(self.total_col) + self.data_table_widget.setHorizontalHeaderLabels(self.column_headers) + header = self.data_table_widget.horizontalHeader() + header.setSectionResizeMode(QtWidgets.QHeaderView.Stretch) + for i in self.resize_content_columns: + header.setSectionResizeMode( + i, QtWidgets.QHeaderView.ResizeToContents) + + if self.need_data_type_choice: + self.data_type = self.data_type_combo_box.currentText() + self.update_data_table_widget_items() + + def update_data_table_widget_items(self): + self.data_list = self.get_data_list() + row_count = len(self.data_list) + row_count = 1 if row_count == 0 else row_count + self.data_table_widget.setRowCount(row_count) + for i in range(len(self.data_list)): + fk = self.check_data_foreign_key(self.data_list[i][0]) + self.add_row(i, fk) + if len(self.data_list) == 0: + """ + No Row, should leave 1 empty row + """ + self.clearFirstRow() + self.update() + + def add_new_row(self): + row_position = self.data_table_widget.rowCount() + self.data_table_widget.insertRow(row_position) + self.add_row(row_position) + self.data_table_widget.scrollToBottom() + self.data_table_widget.repaint() # to show row's header + self.data_table_widget.cellWidget(row_position, 1).setFocus() + + def remove_row(self, remove_row_idx): + self.data_table_widget.removeRow(remove_row_idx) + for i in range(remove_row_idx, self.data_table_widget.rowCount()): + cell_widget = self.data_table_widget.cellWidget(i, 0) + cell_widget.setText(str(i)) + + def cell_input_change(self, changed_text, rowidx, colidx): + """ + If cell's value is changed, text's color will be red + otherwise, text's color will be black + """ + changed = False + if rowidx < len(self.data_list): + if changed_text != self.data_list[rowidx][colidx - 1]: + changed = True + cell_widget = self.data_table_widget.cellWidget(rowidx, colidx) + set_widget_color(cell_widget, changed=changed) + + def check_data_foreign_key(self, val): + if not self.check_fk: + return False + sql = (f"SELECT {self.col_name} FROM channels " + f"WHERE {self.col_name}='{val}'") + param_rows = executeDB(sql) + if len(param_rows) > 0: + return True + else: + return False + + def reset_row_inputs(self, reset, widget_idx, list_idx): + for col_idx in range(1, self.total_col): + cell_widget = self.data_table_widget.cellWidget(widget_idx, col_idx) + read_only = False + if hasattr(cell_widget, 'isReadOnly'): + read_only = cell_widget.isReadOnly() + if not read_only: + if reset == 1: + org_val = self.data_list[list_idx][col_idx - 1] + if isinstance(cell_widget, QtWidgets.QLineEdit): + cell_widget.setText(str(org_val)) + elif isinstance(cell_widget, QtWidgets.QComboBox): + cell_widget.setCurrentText(str(org_val)) + elif isinstance(cell_widget, QtWidgets.QSpinBox): + cell_widget.setValue(int(org_val)) + set_widget_color(cell_widget) + + def add_row(self, row_idx, fk=False): + pass + + def data_type_changed(self): + pass + + def save_changes(self): + try: + self.data_table_widget.focusWidget().clearFocus() + except AttributeError: + pass + self.remove_count = 0 + self.insert_count = 0 + self.skip_count = 0 + row_count = self.data_table_widget.rowCount() + for i in range(row_count): + widget_idx = i - (row_count - self.data_table_widget.rowCount()) + list_idx = i - self.remove_count + self.insert_count - self.skip_count + row_inputs = self.get_row_inputs(widget_idx) + reset = self.update_data(row_inputs, widget_idx, list_idx) + if reset > -1: + self.reset_row_inputs(reset, widget_idx, list_idx) + + def update_data(self, row, widget_idx, list_idx, insert_sql, update_sql): + """ + update data_table_widget and data_list according to the action to + add, remove, update + :param row: values of processed row in data_table_widget + :param widget_idx: index of the process rows in data_table_widget + :param list_idx: index of the process rows in self.data_list + :param col_name: key db column in the table + :param table_name: data table name + :param insert_sql: query to insert a row to the table + :param update_sql: query to update a row in the table + :return -1 for doing nothing + 0 no need to reset input values to org row + 1 need to reset input values to org row + """ + if list_idx < len(self.data_list): + org_row = self.data_list[list_idx] + if row[0] == "": + msg = (f"The {self.col_name} of '{org_row}' at row {widget_idx} " + f"has been changed to blank. Are you sure you want to " + f"delete this row in database?") + result = QtWidgets.QMessageBox.question( + self, "Delete %s?" % org_row, msg, + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No + ) + if result == QtWidgets.QMessageBox.No: + # reset the first widget value and call the function again + # to check other fields + cell_widget = self.data_table_widget.cellWidget(widget_idx, 1) + cell_widget.setText(org_row[0]) + row[0] = org_row[0] + self.update_data(row, widget_idx, list_idx) + else: + sql = (f"DELETE FROM {self.table_name} " + f"WHERE {self.col_name}='{org_row[0]}'") + executeDB(sql) + self.data_list.remove(org_row) + self.remove_row(widget_idx) + self.remove_count += 1 + return -1 + + if row == org_row: + return -1 + if (row[0] in [p[0] for p in self.data_list] and + self.data_list[list_idx][0] != row[0]): + msg = (f"The {self.col_name} of {org_row} at row" + f" {widget_idx} has been changed to '{row[0]}' " + f"which already is in the database. " + f"It will be changed back to {org_row[0]}.") + QtWidgets.QMessageBox.information(self, "Error", msg) + # reset the first widget value and call the function again + # to check other fields + cell_widget = self.data_table_widget.cellWidget(widget_idx, 1) + cell_widget.setText(org_row[0]) + row[0] = org_row[0] + self.update_data(row, widget_idx, list_idx) + else: + msg = (f"{org_row} at row {widget_idx} has " + f"been changed to {row}. Please confirm it.") + result = QtWidgets.QMessageBox.question( + self, "Confirmation", msg, + QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel + ) + if result == QtWidgets.QMessageBox.Cancel: + return 1 + else: + executeDB(update_sql % org_row[0]) + self.data_list[list_idx] = row + return 0 + + if row[0] == "": + msg = (f"Row {widget_idx} has blank {self.col_name}. " + f"It will be removed.") + QtWidgets.QMessageBox.information(self, "Error", msg) + self.remove_row(widget_idx) + return -1 + blank_requested_columns = [self.requested_columns[i] + for i in self.requested_columns.keys() + if row[i - 1] == ""] + if blank_requested_columns != []: + msg = (f"Row {widget_idx}: blank " + f"{', '.join(blank_requested_columns)} which require some " + f"value(s).") + QtWidgets.QMessageBox.information(self, "Error", msg) + return -1 + + if row[0] in [p[0] for p in self.data_list]: + msg = (f"The {self.col_name} '{row[0]}' is already in the database." + f" Row {widget_idx} will be removed.") + QtWidgets.QMessageBox.information(self, "Error", msg) + self.remove_row(widget_idx) + return -1 + executeDB(insert_sql) + self.data_list.append(row) + self.insert_count += 1 + return 0 diff --git a/sohstationviewer/view/core/dbgui_superclass.py b/sohstationviewer/view/core/dbgui_superclass.py deleted file mode 100755 index 45dc5189241ad68adceaf157f2bfbb9d1bf452aa..0000000000000000000000000000000000000000 --- a/sohstationviewer/view/core/dbgui_superclass.py +++ /dev/null @@ -1,368 +0,0 @@ -from PySide2 import QtWidgets, QtGui - -from sohstationviewer.database.proccessDB import executeDB - - -def setWidgetColor(widget, changed=False, readOnly=False): - pallete = QtGui.QPalette() - - if readOnly: - # grey text - pallete.setColor(QtGui.QPalette.Text, QtGui.QColor(100, 100, 100)) - # light blue background - pallete.setColor(QtGui.QPalette.Base, QtGui.QColor(210, 240, 255)) - widget.setReadOnly(True) - widget.setPalette(pallete) - return - else: - # white background - pallete.setColor(QtGui.QPalette.Base, QtGui.QColor(255, 255, 255)) - if changed: - # red text - pallete.setColor(QtGui.QPalette.Text, QtGui.QColor(255, 0, 0)) - else: - try: - if widget.isReadOnly(): - # grey text - pallete.setColor( - QtGui.QPalette.Text, QtGui.QColor(100, 100, 100)) - else: - # black text - pallete.setColor(QtGui.QPalette.Text, QtGui.QColor(0, 0, 0)) - except AttributeError: - pallete.setColor(QtGui.QPalette.Text, QtGui.QColor(0, 0, 0)) - widget.setPalette(pallete) - - -class Ui_DBInfoDialog(QtWidgets.QWidget): - def __init__(self, parent, columnHeaders, colName, tableName, - resizeContentColumns=[], requestedColumns={}, - needDataTypeChoice=False, checkFK=True): - self.totalCol = len(columnHeaders) - self.columnHeaders = columnHeaders - self.resizeContentColumns = resizeContentColumns - self.requestedColumns = requestedColumns - self.needDataTypeChoice = needDataTypeChoice - self.colName = colName - self.tableName = tableName - self.checkFK = checkFK - super(Ui_DBInfoDialog, self).__init__() - - self.setGeometry(100, 100, 900, 900) - mainLayout = QtWidgets.QVBoxLayout() - self.setLayout(mainLayout) - - buttonLayout = self.createButtonsSection() - mainLayout.addLayout(buttonLayout) - - self.createDataTableWidget() - mainLayout.addWidget(self.dataTableWidget, 1) - if self.tableName != '': - instruction = ("Background: LIGHT BLUE - Non Editable due to " - "FK constrain; " - "WHITE - Editable. " - "Text: BLACK - Saved; RED - Not saved") - # TODO: add question mark button to give instruction - # if self.tableName == 'parameters': - # instruction += ( - # "\nValueColors is requested for multiColorDots plotType." - # "Value from low to high" - # "\nThe format for less than or equal value pair is: " - # "value:color" - # "\nThe format for greater than value pair is: " - # "+value:color with " - # "color=R,Y,G,M,C.\n Ex: 2.3:C|+4:M") - - mainLayout.addWidget(QtWidgets.QLabel(instruction)) - - def addWidget(self, dataList, rowidx, colidx, foreignkey=False, - choices=None, range=None, fieldName=''): - if dataList is None: - text = str(rowidx) # row number - else: - text = (str(dataList[rowidx][colidx - 1]) - if rowidx < len(dataList) else '') - - if dataList is None: - widget = QtWidgets.QPushButton(text) - elif choices is None and range is None: - widget = QtWidgets.QLineEdit(text) - if fieldName == 'convertFactor': - # precision=6 - validator = QtGui.QDoubleValidator(0.0, 5.0, 6) - validator.setNotation(QtGui.QDoubleValidator.StandardNotation) - widget.setValidator(validator) - if text == '': - widget.setText('1') - elif choices: - widget = QtWidgets.QComboBox() - widget.addItems(choices) - widget.setCurrentText(text) - elif range: - widget = QtWidgets.QSpinBox() - widget.setMinimum(range[0]) - widget.setMaximum(range[1]) - if text in ["", None, 'None']: - widget.setValue(range[0]) - else: - widget.setValue(int(text)) - - if dataList is None: - setWidgetColor(widget) - widget.setFixedWidth(40) - widget.clicked.connect( - lambda: self.rowNumberClicked(widget)) - elif foreignkey: - setWidgetColor(widget, readOnly=True) - else: - new = False if rowidx < len(dataList) else True - setWidgetColor(widget, readOnly=False, changed=new) - if choices is None and range is None: - widget.textChanged.connect( - lambda changedtext: - self.cellInputChange(changedtext, rowidx, colidx)) - elif choices: - widget.currentTextChanged.connect( - lambda changedtext: - self.cellInputChange(changedtext, rowidx, colidx)) - elif range: - widget.valueChanged.connect( - lambda changedtext: - self.cellInputChange(changedtext, rowidx, colidx)) - self.dataTableWidget.setCellWidget(rowidx, colidx, widget) - - def rowNumberClicked(self, widget): - self.dataTableWidget.selectRow(int(widget.text())) - self.dataTableWidget.repaint() - - def createButtonsSection(self): - hLayout = QtWidgets.QHBoxLayout() - - if self.needDataTypeChoice: - self.dataTypeCombobox = QtWidgets.QComboBox(self) - dataTypeRows = executeDB('SELECT * FROM DataTypes') - self.dataTypeCombobox.addItems([d[0] for d in dataTypeRows]) - self.dataTypeCombobox.currentTextChanged.connect( - self.dataTypeChanged) - hLayout.addWidget(self.dataTypeCombobox) - if self.tableName != '': - self.addRowBtn = QtWidgets.QPushButton(self, text='ADD ROW') - self.addRowBtn.setFixedWidth(300) - self.addRowBtn.clicked.connect(self.addNewRow) - hLayout.addWidget(self.addRowBtn) - - self.saveChangesBtn = QtWidgets.QPushButton( - self, text='SAVE CHANGES') - self.saveChangesBtn.setFixedWidth(300) - self.saveChangesBtn.clicked.connect(self.saveChanges) - hLayout.addWidget(self.saveChangesBtn) - - self.closeBtn = QtWidgets.QPushButton(self, text='CLOSE') - self.closeBtn.setFixedWidth(300) - self.closeBtn.clicked.connect(self.close) - hLayout.addWidget(self.closeBtn) - return hLayout - - def createDataTableWidget(self): - self.dataTableWidget = QtWidgets.QTableWidget(self) - self.dataTableWidget.verticalHeader().hide() - self.dataTableWidget.setColumnCount(self.totalCol) - self.dataTableWidget.setHorizontalHeaderLabels(self.columnHeaders) - header = self.dataTableWidget.horizontalHeader() - header.setSectionResizeMode(QtWidgets.QHeaderView.Stretch) - for i in self.resizeContentColumns: - header.setSectionResizeMode( - i, QtWidgets.QHeaderView.ResizeToContents) - - if self.needDataTypeChoice: - self.dataType = self.dataTypeCombobox.currentText() - self.updateDataTableWidgetItems() - - def updateDataTableWidgetItems(self): - self.dataList = self.getDataList() - rowCount = len(self.dataList) - rowCount = 1 if rowCount == 0 else rowCount - self.dataTableWidget.setRowCount(rowCount) - for i in range(len(self.dataList)): - fk = self.checkDataForeignKey(self.dataList[i][0]) - self.addRow(i, fk) - if len(self.dataList) == 0: - """ - No Row, should leave 1 empty row - """ - self.clearFirstRow() - self.update() - - def addNewRow(self): - rowPosition = self.dataTableWidget.rowCount() - self.dataTableWidget.insertRow(rowPosition) - self.addRow(rowPosition) - self.dataTableWidget.scrollToBottom() - self.dataTableWidget.repaint() # to show row's header - self.dataTableWidget.cellWidget(rowPosition, 1).setFocus() - - def removeRow(self, removeRowidx): - self.dataTableWidget.removeRow(removeRowidx) - for i in range(removeRowidx, self.dataTableWidget.rowCount()): - cellWget = self.dataTableWidget.cellWidget(i, 0) - cellWget.setText(str(i)) - - def cellInputChange(self, changedText, rowidx, colidx): - """ - If cell's value is changed, text's color will be red - otherwise, text's color will be black - """ - changed = False - if rowidx < len(self.dataList): - if changedText != self.dataList[rowidx][colidx - 1]: - changed = True - cellWget = self.dataTableWidget.cellWidget(rowidx, colidx) - setWidgetColor(cellWget, changed=changed) - - def checkDataForeignKey(self, val): - if not self.checkFK: - return False - sql = (f"SELECT {self.colName} FROM channels " - f"WHERE {self.colName}='{val}'") - paramRows = executeDB(sql) - if len(paramRows) > 0: - return True - else: - return False - - def resetRowInputs(self, reset, widgetidx, listidx): - for colidx in range(1, self.totalCol): - cellWget = self.dataTableWidget.cellWidget(widgetidx, colidx) - readOnly = False - if hasattr(cellWget, 'isReadOnly'): - readOnly = cellWget.isReadOnly() - if not readOnly: - if reset == 1: - orgVal = self.dataList[listidx][colidx - 1] - if isinstance(cellWget, QtWidgets.QLineEdit): - cellWget.setText(str(orgVal)) - elif isinstance(cellWget, QtWidgets.QComboBox): - cellWget.setCurrentText(str(orgVal)) - elif isinstance(cellWget, QtWidgets.QSpinBox): - cellWget.setValue(int(orgVal)) - setWidgetColor(cellWget) - - def addRow(self, rowidx, fk=False): - pass - - def dataTypeChanged(self): - pass - - def saveChanges(self): - try: - self.dataTableWidget.focusWidget().clearFocus() - except AttributeError: - pass - self.removeCount = 0 - self.insertCount = 0 - self.skipCount = 0 - rowCount = self.dataTableWidget.rowCount() - for i in range(rowCount): - widgetidx = i - (rowCount - self.dataTableWidget.rowCount()) - listidx = i - self.removeCount + self.insertCount - self.skipCount - rowInputs = self.getRowInputs(widgetidx) - reset = self.updateData(rowInputs, widgetidx, listidx) - if reset > -1: - self.resetRowInputs(reset, widgetidx, listidx) - - def updateData(self, row, widgetidx, listidx, insertsql, updatesql): - """ - update dataTableWidget and dataList according to the action to - add, remove, update - :param row: values of processed row in dataTableWidget - :param widgetidx: index of the process rows in dataTableWidget - :param listidx: index of the process rows in self.dataList - :param colName: key db column in the table - :param tableName: data table name - :param insertsql: query to insert a row to the table - :param updatesql: query to update a row in the table - :return -1 for doing nothing - 0 no need to reset input values to org row - 1 need to reset input values to org row - """ - if listidx < len(self.dataList): - orgRow = self.dataList[listidx] - if row[0] == "": - msg = (f"The {self.colName} of '{orgRow}' at row {widgetidx} " - f"has been changed to blank. Are you sure you want to " - f"delete this row in database?") - result = QtWidgets.QMessageBox.question( - self, "Delete %s?" % orgRow, msg, - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No - ) - if result == QtWidgets.QMessageBox.No: - # reset the first widget value and call the function again - # to check other fields - cellWget = self.dataTableWidget.cellWidget(widgetidx, 1) - cellWget.setText(orgRow[0]) - row[0] = orgRow[0] - self.updateData(row, widgetidx, listidx) - else: - sql = (f"DELETE FROM {self.tableName} " - f"WHERE {self.colName}='{orgRow[0]}'") - executeDB(sql) - self.dataList.remove(orgRow) - self.removeRow(widgetidx) - self.removeCount += 1 - return -1 - - if row == orgRow: - return -1 - if (row[0] in [p[0] for p in self.dataList] and - self.dataList[listidx][0] != row[0]): - msg = (f"The {self.colName} of {orgRow} at row" - f" {widgetidx} has been changed to '{row[0]}' " - f"which already is in the database. " - f"It will be changed back to {orgRow[0]}.") - QtWidgets.QMessageBox.information(self, "Error", msg) - # reset the first widget value and call the function again - # to check other fields - cellWget = self.dataTableWidget.cellWidget(widgetidx, 1) - cellWget.setText(orgRow[0]) - row[0] = orgRow[0] - self.updateData(row, widgetidx, listidx) - else: - msg = (f"{orgRow} at row {widgetidx} has " - f"been changed to {row}. Please confirm it.") - result = QtWidgets.QMessageBox.question( - self, "Confirmation", msg, - QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel - ) - if result == QtWidgets.QMessageBox.Cancel: - return 1 - else: - executeDB(updatesql % orgRow[0]) - self.dataList[listidx] = row - return 0 - - if row[0] == "": - msg = (f"Row {widgetidx} has blank {self.colName}. " - f"It will be removed.") - QtWidgets.QMessageBox.information(self, "Error", msg) - self.removeRow(widgetidx) - return -1 - blankRequestedColumns = [self.requestedColumns[i] - for i in self.requestedColumns.keys() - if row[i-1] == ""] - if blankRequestedColumns != []: - msg = (f"Row {widgetidx}: blank " - f"{', '.join(blankRequestedColumns)} which require some " - f"value(s).") - QtWidgets.QMessageBox.information(self, "Error", msg) - return -1 - - if row[0] in [p[0] for p in self.dataList]: - msg = (f"The {self.colName} '{row[0]}' is already in the database." - f" Row {widgetidx} will be removed.") - QtWidgets.QMessageBox.information(self, "Error", msg) - self.removeRow(widgetidx) - return -1 - executeDB(insertsql) - self.dataList.append(row) - self.insertCount += 1 - return 0 diff --git a/sohstationviewer/view/dataTypedialog.py b/sohstationviewer/view/dataTypedialog.py deleted file mode 100755 index a546a2ddfb15fa2c90a001cbe229bb4e8d118d4a..0000000000000000000000000000000000000000 --- a/sohstationviewer/view/dataTypedialog.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -datatypedialog.py -GUI to add/edit/remove dataTypes -NOTE: Cannot remove or change dataTypes that already have channels. -""" - -from sohstationviewer.view.core.dbgui_superclass import Ui_DBInfoDialog -from sohstationviewer.database.proccessDB import executeDB - - -class DataTypeDialog(Ui_DBInfoDialog): - def __init__(self, parent): - super().__init__(parent, ['No.', 'DataType'], 'dataType', 'dataTypes', - resizeContentColumns=[0]) - self.setWindowTitle("Edit/Add/Delete DataTypes") - - def addRow(self, rowidx, fk=False): - self.addWidget(None, rowidx, 0) # No. - self.addWidget(self.dataList, rowidx, 1, foreignkey=fk) - - def getDataList(self): - dataTypeRows = executeDB('SELECT * FROM DataTypes') - return [[d[0]] for d in dataTypeRows] - - def getRowInputs(self, rowidx): - return [self.dataTableWidget.cellWidget(rowidx, 1).text().strip()] - - def updateData(self, row, widgetidx, listidx): - insertsql = (f"INSERT INTO DataTypes VALUES('{row[0]}')") - updatesql = (f"UPDATE DataTypes SET dataType='{row[0]}' " - f"WHERE dataType='%s'") - - return super().updateData( - row, widgetidx, listidx, insertsql, updatesql) diff --git a/sohstationviewer/view/data_type_dialog.py b/sohstationviewer/view/data_type_dialog.py new file mode 100755 index 0000000000000000000000000000000000000000..726dbb4c5f33f672a0b83119938333326d9eff73 --- /dev/null +++ b/sohstationviewer/view/data_type_dialog.py @@ -0,0 +1,34 @@ +""" +datatypedialog.py +GUI to add/edit/remove dataTypes +NOTE: Cannot remove or change dataTypes that already have channels. +""" + +from sohstationviewer.view.core.db_gui_superclass import Ui_DBInfoDialog +from sohstationviewer.database.proccessDB import executeDB + + +class DataTypeDialog(Ui_DBInfoDialog): + def __init__(self, parent): + super().__init__(parent, ['No.', 'DataType'], 'dataType', 'dataTypes', + resize_content_columns=[0]) + self.setWindowTitle("Edit/Add/Delete DataTypes") + + def add_row(self, row_idx, fk=False): + self.addWidget(None, row_idx, 0) # No. + self.addWidget(self.data_list, row_idx, 1, foreign_key=fk) + + def get_data_list(self): + data_type_rows = executeDB('SELECT * FROM DataTypes') + return [[d[0]] for d in data_type_rows] + + def get_row_inputs(self, row_idx): + return [self.data_table_widget.cellWidget(row_idx, 1).text().strip()] + + def update_data(self, row, widget_idx, list_idx): + insert_sql = f"INSERT INTO DataTypes VALUES('{row[0]}')" + update_sql = (f"UPDATE DataTypes SET dataType='{row[0]}' " + f"WHERE dataType='%s'") + + return super().update_data( + row, widget_idx, list_idx, insert_sql, update_sql) diff --git a/sohstationviewer/view/mainwindow.py b/sohstationviewer/view/mainwindow.py index 88b0515dda1a07c27897411a3e2522708cfb23c8..d5767b6a8b5f07c48251f60a2bbc8623a9344c3d 100755 --- a/sohstationviewer/view/mainwindow.py +++ b/sohstationviewer/view/mainwindow.py @@ -9,11 +9,11 @@ from sohstationviewer.view.ui.main_ui import Ui_MainWindow from sohstationviewer.view.calendar_dialog import CalendarDialog from sohstationviewer.view.core.filelistwidget import FileListItem from sohstationviewer.controller.processing import loadData, detectDataType -from sohstationviewer.view.dataTypedialog import DataTypeDialog -from sohstationviewer.view.paramdialog import ParamDialog -from sohstationviewer.view.channeldialog import ChannelDialog -from sohstationviewer.view.plottypedialog import PlotTypeDialog -from sohstationviewer.view.channelpreferdialog import ChannelPreferDialog +from sohstationviewer.view.data_type_dialog import DataTypeDialog +from sohstationviewer.view.param_dialog import ParamDialog +from sohstationviewer.view.channel_dialog import ChannelDialog +from sohstationviewer.view.plot_type_dialog import PlotTypeDialog +from sohstationviewer.view.channel_prefer_dialog import ChannelPreferDialog from sohstationviewer.database.proccessDB import executeDB_dict from sohstationviewer.view.waveformdialog import WaveformDialog from sohstationviewer.view.time_power_squareddialog import ( diff --git a/sohstationviewer/view/param_dialog.py b/sohstationviewer/view/param_dialog.py new file mode 100755 index 0000000000000000000000000000000000000000..8cd871d967130e77ace370ed8635da044eda2b6a --- /dev/null +++ b/sohstationviewer/view/param_dialog.py @@ -0,0 +1,68 @@ +""" +param_dialog.py +GUI to add/dit/remove params +NOTE: Cannot remove or change params that are already used for channels. +""" + +from PySide2 import QtWidgets + + +from sohstationviewer.view.core.db_gui_superclass import Ui_DBInfoDialog +from sohstationviewer.view.core import plottingWidget +from sohstationviewer.database.proccessDB import executeDB + +from sohstationviewer.conf.dbSettings import dbConf + + +class ParamDialog(Ui_DBInfoDialog): + def __init__(self, parent): + super().__init__( + parent, + ['No.', 'Param', 'Plot Type', 'ValueColors', 'Height '], + 'param', 'parameters', + resize_content_columns=[0, 3]) + self.setWindowTitle("Edit/Add/Delete Parameters") + + def add_row(self, row_idx, fk=False): + self.addWidget(None, row_idx, 0) # No. + self.addWidget(self.data_list, row_idx, 1, foreign_key=fk) + self.addWidget(self.data_list, row_idx, 2, + choices=[''] + sorted(plottingWidget.plotFunc.keys())) + + self.addWidget(self.data_list, row_idx, 3) + self.addWidget(self.data_list, row_idx, 4, range=[0, 10]) + + def get_data_list(self): + param_rows = executeDB('SELECT * FROM Parameters') + return [[d[0], + '' if d[1] is None else d[1], + d[2]] + for d in param_rows] + + def get_row_inputs(self, row_idx): + # check value_colors string + value_colors_string = self.data_table_widget.cellWidget( + row_idx, 3).currentText().strip() + value_colors = value_colors_string.split("|") + for vc in value_colors: + if not dbConf['valColRE'].match(vc): + msg = (f"The valueColor is requested for '{vc}' at line " + f"{row_idx}does not match the required format:" + f"[+]value:color with color=R,Y,G,M,C." + f"\n Ex: 2.3:C|+4:M") + QtWidgets.QMessageBox.information(self, "Error", msg) + return [ + self.data_table_widget.cellWidget(row_idx, 1).text().strip(), + self.data_table_widget.cellWidget(row_idx, 2).currentText().strip(), + self.data_table_widget.cellWidget(row_idx, 3).currentText().strip(), + int(self.data_table_widget.cellWidget(row_idx, 4).text()) + ] + + def update_data(self, row, widget_idx, list_idx): + insertsql = (f"INSERT INTO Parameters VALUES" + f"('{row[0]}', '{row[1]}', {row[2]})") + updatesql = (f"UPDATE Parameters SET param='{row[0]}', " + f"plotType='{row[1]}', height={row[2]} " + f"WHERE param='%s'") + return super().update_data( + row, widget_idx, list_idx, insertsql, updatesql) diff --git a/sohstationviewer/view/paramdialog.py b/sohstationviewer/view/paramdialog.py deleted file mode 100755 index 0315073a7ff1589cd15faaeaf81e1256d071c433..0000000000000000000000000000000000000000 --- a/sohstationviewer/view/paramdialog.py +++ /dev/null @@ -1,68 +0,0 @@ -""" -paramdialog.py -GUI to add/dit/remove params -NOTE: Cannot remove or change params that are already used for channels. -""" - -from PySide2 import QtWidgets - - -from sohstationviewer.view.core.dbgui_superclass import Ui_DBInfoDialog -from sohstationviewer.view.core import plottingWidget -from sohstationviewer.database.proccessDB import executeDB - -from sohstationviewer.conf.dbSettings import dbConf - - -class ParamDialog(Ui_DBInfoDialog): - def __init__(self, parent): - super().__init__( - parent, - ['No.', 'Param', 'Plot Type', 'ValueColors', 'Height '], - 'param', 'parameters', - resizeContentColumns=[0, 3]) - self.setWindowTitle("Edit/Add/Delete Parameters") - - def addRow(self, rowidx, fk=False): - self.addWidget(None, rowidx, 0) # No. - self.addWidget(self.dataList, rowidx, 1, foreignkey=fk) - self.addWidget(self.dataList, rowidx, 2, - choices=[''] + sorted(plottingWidget.plotFunc.keys())) - - self.addWidget(self.dataList, rowidx, 3) - self.addWidget(self.dataList, rowidx, 4, range=[0, 10]) - - def getDataList(self): - paramRows = executeDB('SELECT * FROM Parameters') - return [[d[0], - '' if d[1] is None else d[1], - d[2]] - for d in paramRows] - - def getRowInputs(self, rowidx): - # check vallueColors string - valueColorsString = self.dataTableWidget.cellWidget( - rowidx, 3).currentText().strip() - valueColors = valueColorsString.split("|") - for vc in valueColors: - if not dbConf['valColRE'].match(vc): - msg = (f"The valueColor is requested for '{vc}' at line " - f"{rowidx}does not match the required format:" - f"[+]value:color with color=R,Y,G,M,C." - f"\n Ex: 2.3:C|+4:M") - QtWidgets.QMessageBox.information(self, "Error", msg) - return [ - self.dataTableWidget.cellWidget(rowidx, 1).text().strip(), - self.dataTableWidget.cellWidget(rowidx, 2).currentText().strip(), - self.dataTableWidget.cellWidget(rowidx, 3).currentText().strip(), - int(self.dataTableWidget.cellWidget(rowidx, 4).text()) - ] - - def updateData(self, row, widgetidx, listidx): - insertsql = (f"INSERT INTO Parameters VALUES" - f"('{row[0]}', '{row[1]}', {row[2]})") - updatesql = (f"UPDATE Parameters SET param='{row[0]}', " - f"plotType='{row[1]}', height={row[2]} " - f"WHERE param='%s'") - return super().updateData( - row, widgetidx, listidx, insertsql, updatesql) diff --git a/sohstationviewer/view/plottypedialog.py b/sohstationviewer/view/plot_type_dialog.py similarity index 54% rename from sohstationviewer/view/plottypedialog.py rename to sohstationviewer/view/plot_type_dialog.py index 4611d3eef0d7e87c0050a61f33f7dfecafd84709..7f560d65c493ec5606b0e632c91b0b313c3b6359 100755 --- a/sohstationviewer/view/plottypedialog.py +++ b/sohstationviewer/view/plot_type_dialog.py @@ -4,7 +4,7 @@ GUI to view the types of plotting and their descriptions NOTE: plottypes are defined in plottingWidget """ -from sohstationviewer.view.core.dbgui_superclass import Ui_DBInfoDialog +from sohstationviewer.view.core.db_gui_superclass import Ui_DBInfoDialog from sohstationviewer.view.core import plottingWidget @@ -12,13 +12,13 @@ class PlotTypeDialog(Ui_DBInfoDialog): def __init__(self, parent): super().__init__( parent, ['No.', ' Plot Type ', 'Description'], - '', '', resizeContentColumns=[0, 1], checkFK=False) + '', '', resize_content_columns=[0, 1], check_fk=False) self.setWindowTitle("Plotting Types") - def addRow(self, rowidx, fk=False): - self.addWidget(None, rowidx, 0) # No. - self.addWidget(self.dataList, rowidx, 1, foreignkey=True) - self.addWidget(self.dataList, rowidx, 2, foreignkey=True) + def add_row(self, row_idx, fk=False): + self.addWidget(None, row_idx, 0) # No. + self.addWidget(self.data_list, row_idx, 1, foreign_key=True) + self.addWidget(self.data_list, row_idx, 2, foreign_key=True) - def getDataList(self): + def get_data_list(self): return [[key, val[0]] for key, val in plottingWidget.plotFunc.items()]