class ModuleOperationWidget(QWidget): def __init__(self, parent, dataModel): super(QWidget, self).__init__(parent) self.dataModel = dataModel self.layout = QHBoxLayout(self) self.supported = [] self.unsupported = [] self.moduleOperationGroup = QGroupBox("Classification Operation") self.moduleOperationLayout = QVBoxLayout() self.moduleOperationGroup.setLayout(self.moduleOperationLayout) self.dataTypeCheck = PandasDatatypeCheck() self.selectLabelDropDown = QComboBox() self.selectLabelDropDown.currentIndexChanged.connect( self.label_selection_changed) self.buttonOperationLayout = QHBoxLayout() self.startOperationButton = QPushButton("Start") self.stopOperationButton = QPushButton("Stop") self.buttonOperationLayout.addWidget(self.startOperationButton) self.buttonOperationLayout.addWidget(self.stopOperationButton) self.startOperationButton.clicked.connect(self.start_operation) self.stopOperationButton.clicked.connect(self.stop_operation) self.moduleOperationLayout.addWidget(self.selectLabelDropDown) self.moduleOperationLayout.addLayout(self.buttonOperationLayout) self.startOperationButton.setEnabled(False) self.stopOperationButton.setEnabled(False) self.layout.addWidget(self.moduleOperationGroup) self.setLayout(self.layout) def start_operation(self): self.selectLabelDropDown.setEnabled(False) self.startOperationButton.setEnabled(False) self.stopOperationButton.setEnabled(True) self.parent().start_operation() def stop_operation(self): self.selectLabelDropDown.setEnabled(True) self.startOperationButton.setEnabled(True) self.stopOperationButton.setEnabled(False) self.parent().stop_operation() def update_supported_operation_type(self, supported, unsupported): self.supported = supported self.unsupported = unsupported if self.selectLabelDropDown.currentIndex() < len( self.headerIndex) and self.selectLabelDropDown.currentIndex( ) >= 0: self.label_selection_changed( self.selectLabelDropDown.currentIndex()) def label_selection_changed(self, i): self.stopOperationButton.setEnabled(False) self.startOperationButton.setEnabled(False) if i >= 0 and i < len(self.headerIndex): if str(self.headerIndex[i]["data_type"]) in self.supported: self.startOperationButton.setEnabled(True) self.parent().set_label(i) def set_label_drop_down(self): self.data = self.dataModel.get_data() self.selectLabelDropDown.clear() self.headers = list(self.data.columns) self.headerIndex = {} for index in range(len(self.headers)): label_data = self.data.iloc[:, index] self.dataTypeCheck.setDataType(label_data) dataType = self.dataTypeCheck.getDataType() self.headerIndex[index] = { "name": self.headers[index], "data_type": dataType } self.selectLabelDropDown.addItem("(" + str(dataType) + ") " + self.headers[index])
class DatasetEditor(QDialog): def __init__(self, parent, data_model, parent_history): super(QDialog, self).__init__(parent) self.dataModel = data_model self.data = None self.dataTypeCheck = PandasDatatypeCheck() self.isUndoConfirmed = False self.dataHistory = DataHistory() self.parentHistory = parent_history self.layout = QVBoxLayout() self.btnLayout = QHBoxLayout() self.addInstanceButton = QPushButton("Add Instance") self.undoButton = QPushButton("Undo") self.saveButton = QPushButton("Save") self.cancelButton = QPushButton("Cancel") self.addInstanceButton.clicked.connect(self._add_instance) self.undoButton.clicked.connect(self._undo_confirmation) self.saveButton.clicked.connect(self.on_ok) self.cancelButton.clicked.connect(self.on_cancel) self.btnLayout.addWidget(self.addInstanceButton) self.btnLayout.addWidget(self.undoButton) self.btnLayout.addWidget(self.saveButton) self.btnLayout.addWidget(self.cancelButton) self.undoButton.setEnabled(False) self.dataAttributeTable = PandasTableView({}, 0, 0) self.dataAttributeTable.setSelectionBehavior(QAbstractItemView.SelectItems) self.dataAttributeTable.setEditTriggers(QAbstractItemView.DoubleClicked) self.dataAttributeTable.setSelectionBehavior(QAbstractItemView.SelectRows) self.dataAttributeTable.setContextMenuPolicy(Qt.CustomContextMenu) self.dataAttributeTable.customContextMenuRequested.connect(self.showContextMenu) self.dataAttributeTable.cellChanged.connect(self._data_changed) self.layout.addWidget(self.dataAttributeTable) self.layout.addLayout(self.btnLayout) self.setMinimumSize(600, 400) self.setWindowTitle("Maleo Editor") self.setLayout(self.layout) def showContextMenu(self, pos): menu = QMenu() clear_action = menu.addAction("Undo") add_instance = menu.addAction("Add Instance") delete_instance = menu.addAction("Delete Selected Instance") delete_all_instance = menu.addAction("Delete ALL Selected Instance") if self.dataHistory.is_past_empty(): clear_action.setEnabled(False) else: clear_action.setEnabled(True) action = menu.exec_(self.mapToGlobal(pos)) if action == clear_action: self._undo_confirmation() elif action == add_instance: self._add_instance() elif action == delete_instance: self._delete_instances(False) elif action == delete_all_instance: self._delete_instances(True) def _delete_instances(self, is_all): selected_rows = self.dataAttributeTable.selectionModel().selectedRows() num_deleted_row = 0 for selected_row in selected_rows: row = selected_row.row() self.dataHistory.append_data({"row": row, "col": "all_deletion", "value": self.data.copy()}) self.data.drop(index=row - num_deleted_row, axis=0, inplace=True) self.data.reset_index(inplace=True, drop=True) self.dataAttributeTable.deleteRow(row - num_deleted_row) num_deleted_row += 1 if not is_all: break self.update_status() def _undo_confirmation(self): if self.isUndoConfirmed: self._undo_operation() else: dlg = QMessageBox() dlg.setIcon(QMessageBox.Question) dlg.setWindowTitle("Confirmation") dlg.setText("Are you sure you want to undo the last action ? This action cannot be undone.") dlg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) dlg.setDefaultButton(QMessageBox.No) button_yes = dlg.button(QMessageBox.Yes) button_yes.setText("Yes") button_no = dlg.button(QMessageBox.No) button_no.setText("No") dlg.exec_() if dlg.clickedButton() == button_yes: self.isUndoConfirmed = True self._undo_operation() def load_data(self): self.data = self.dataModel.get_copy() try: self._show_table() except Exception as e: self.parent().parent().dialog_critical("Error exception !"+str(e)) def update_status(self): if self.dataHistory.is_past_empty(): self.undoButton.setEnabled(False) else: self.undoButton.setEnabled(True) def _undo_operation(self): data = self.dataHistory.pop_past() self.update_status() self._setTableItemData(data["row"], data["col"], data["value"]) def _setTableItemData(self, row, col, value): if col == "all_insertion": self.data.drop(index=row, axis=0, inplace=True) self.data.reset_index(inplace=True, drop=True) self.dataAttributeTable.cellChanged.disconnect() self.dataAttributeTable.deleteRow(row) self.dataAttributeTable.cellChanged.connect(self._data_changed) elif col == "all_deletion": self.data = value self._show_table() else: self.data.iat[row, col] = value self.dataAttributeTable.cellChanged.disconnect() self.dataAttributeTable.setItemData(row, col, value) self.dataAttributeTable.cellChanged.connect(self._data_changed) def _show_table(self): if not self.dataModel.is_empty(): self._draw_table(self.data, self.data.shape[0], self.data.shape[1]) else: self._draw_table({}, 0, 0) def _draw_table(self, data, row, col): self.dataAttributeTable.updateParameter(col, row) self.dataAttributeTable.cellChanged.disconnect() self.dataAttributeTable.updateData(data) self.dataAttributeTable.cellChanged.connect(self._data_changed) self.dataAttributeTable.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) def _append_table_row(self, new_row): row_count = self.dataAttributeTable.rowCount() self.dataHistory.append_data({"row": row_count, "col": "all_insertion", "value": None}) self.update_status() self.data = self.data.append(new_row, ignore_index=True) self.dataAttributeTable.cellChanged.disconnect() self.dataAttributeTable.appendRow(new_row) self.dataAttributeTable.cellChanged.connect(self._data_changed) def _data_changed(self, row, col): changed_column = self.data.iloc[:, col] self.dataTypeCheck.setDataType(changed_column) self.dataType = self.dataTypeCheck.getDataType() item = self.dataAttributeTable.item(row, col).text() try: if self.dataType == self.dataTypeCheck.getType().Numeric: if nconvert.is_num(item): self.dataHistory.append_data({"row": row, "col": col, "value": self.data.iat[row, col].copy()}) self.data.iat[row, col] = nconvert.str_to_num(item) self.update_status() else: self.parent().parent().dialog_critical("Cannot update numeric column by nominal value !") self.dataAttributeTable.setItemData(row, col, self.data.iat[row, col]) elif self.dataType == self.dataTypeCheck.getType().Nominal: if not nconvert.is_num(item): self.dataHistory.append_data({"row": row, "col": col, "value": self.data.iat[row, col]}) self.data.iat[row, col] = item self.update_status() else: self.parent().parent().dialog_critical("Cannot update nominal column by numeric value !") self.dataAttributeTable.setItemData(row, col, self.data.iat[row, col]) else: self.parent().parent().dialog_critical("Column datatype is unknown !") print("This col item is unknown") except Exception as e: self.parent().parent().dialog_critical("Error exception !\n"+str(e)) def _add_instance(self): new_row = {} for column in self.data.columns: changed_column = self.data.loc[:, column] self.dataTypeCheck.setDataType(changed_column) self.dataType = self.dataTypeCheck.getDataType() if self.dataType == self.dataTypeCheck.getType().Numeric: new_row[column] = 0 elif self.dataType == self.dataTypeCheck.getType().Nominal: new_row[column] = "" else: new_row[column] = "" self._append_table_row(new_row) def on_ok(self): self.parentHistory.append_data(self.dataModel.get_copy()) self.dataModel.set_data(self.data) self.parent().parent().data_loaded() self.close() def on_cancel(self): self.close()
class VisualizationTab(QWidget): def __init__(self, parent, data_model, model_results, screen_height, screen_width): super(QWidget, self).__init__(parent) self.data = None self.dataModel = data_model self.dataTypeCheck = PandasDatatypeCheck() self.layout = QVBoxLayout() self.layout.setSpacing(0) self.actionLayout = QHBoxLayout() self.actionLayout.setSpacing(0) self.selectLabelDropDown = QComboBox() self.selectLabelDropDown.currentIndexChanged.connect(self.labelSelectionChanged) self.actionLayout.addWidget(self.selectLabelDropDown) self.graphLayout = QGridLayout(self) self.graphLayout.setSpacing(0) self.widthWidget = 4 pg.setConfigOption('background', 'w') pg.setConfigOption('foreground', 'k') self.visualizationWidgets = [] self.layout.addLayout(self.actionLayout) self.layout.addLayout(self.graphLayout) self.setLayout(self.layout) def set_label_drop_down(self): self.selectLabelDropDown.clear() self.headers = list(self.data.columns) for index in range(len(self.headers)): label_data = self.data.iloc[:, index] self.dataTypeCheck.setDataType(label_data) dataType = self.dataTypeCheck.getDataType() self.selectLabelDropDown.addItem("("+str(dataType)+") "+self.headers[index]) def labelSelectionChanged(self, i): self.labels = self.data.iloc[:, i] def load_data(self): self.data = self.dataModel.get_data() if not self.dataModel.is_empty(): try: self.set_label_drop_down() self.plot() except Exception as e: self.dialog_critical("Error exception "+str(e)) else: self.dialog_critical("Data is empty !") def plot(self): for widget in self.visualizationWidgets: self.graphLayout.removeWidget(widget) widget.deleteLater() widget = None self.visualizationWidgets = [] row = 0 col = 0 for column in self.data: plot = pg.PlotWidget() value = self.data[column].value_counts() x = [i for i in value.keys()] y = [j for i, j in value.items()] if np.array(x).dtype.char == 'U': x = [i for i in range(len(x))] bar = pg.BarGraphItem(x=x, height=y, width=0.1, brush='b', pen=pg.mkPen('b', width=1)) plot.addItem(bar) plot.setTitle(column) # plot.set_label('left', 'Value') # plot.set_label('bottom', 'Range') self.visualizationWidgets.append(plot) self.graphLayout.addWidget(plot, row, col) col += 1 if col == 4: col = 0 row += 1 def dialog_critical(self, message): dlg = QMessageBox(self) dlg.setText(message) dlg.setIcon(QMessageBox.Critical) dlg.show()