def test_set_db_loc(self): """ Verifies that the database location can be set """ import os data_dir = os.getcwd() settings = RaseSettings() settings.setDataDirectory(data_dir) assert data_dir == settings.getDataDirectory()
def complete_tests(): """Make sure no sample spectra are left after the final test is run""" settings = RaseSettings() original_data_dir = settings.getDataDirectory() settings.setDataDirectory(os.path.join(os.getcwd(), '__temp_test_rase')) yield None # anything before this line will be run prior to the tests print('CLEANING UP') # run after the last test settings = RaseSettings() if os.path.isdir(settings.getSampleDirectory()): shutil.rmtree(settings.getSampleDirectory()) if os.path.isfile(settings.getDatabaseFilepath()): os.remove(settings.getDatabaseFilepath()) settings.setDataDirectory(original_data_dir) print('CLEANUP COMPLETE')
class DetailedResultsDialog(ui_detailed_results_dialog.Ui_dlgDetailedResults, QDialog): def __init__(self, resultMap): QDialog.__init__(self) self.setupUi(self) self.settings = RaseSettings() self.headerLabels = [ 'File', 'Tp', 'Fn', 'Fp', 'Precision', 'Recall', 'Fscore', 'IDs' ] self.NUM_COLS = len(self.headerLabels) self.session = Session() self.tblDetailedResults.setContextMenuPolicy(Qt.CustomContextMenu) self.tblDetailedResults.setColumnCount(self.NUM_COLS) self.tblDetailedResults.setHorizontalHeaderLabels(self.headerLabels) self.tblDetailedResults.horizontalHeader().setSectionResizeMode( 0, QHeaderView.Stretch) self.tblDetailedResults.setRowCount(0) row = 0 for file in resultMap: results = resultMap[file] results.insert(0, file) self.tblDetailedResults.insertRow(row) for col in range(self.NUM_COLS): self.tblDetailedResults.setItem(row, col, QTableWidgetItem(results[col])) row = row + 1 results.pop(0) self.tblDetailedResults.resizeColumnsToContents() self.tblDetailedResults.horizontalHeader().setSectionResizeMode( 0, QHeaderView.Stretch) self.buttonOK.clicked.connect(self.closeSelected) self.buttonExport.clicked.connect(self.handleExport) def handleExport(self): """ exports to CSV """ path = QFileDialog.getSaveFileName(self, 'Save File', self.settings.getDataDirectory(), 'CSV (*.csv)') if path[0]: with open(path[0], mode='w', newline='') as stream: writer = csv.writer(stream) writer.writerow(self.headerLabels) for row in range(self.tblDetailedResults.rowCount()): rowdata = [] for column in range(self.tblDetailedResults.columnCount()): item = self.tblDetailedResults.item(row, column) if item is not None: rowdata.append(item.text()) else: rowdata.append('') writer.writerow(rowdata) def get_selected_cells_as_text(self): """ Returns the selected cells of the table as plain text """ selected_rows = self.tblDetailedResults.selectedIndexes() text = "" # show the context menu only if on an a valid part of the table if selected_rows: cols = set(index.column() for index in self.tblDetailedResults.selectedIndexes()) for row in set( index.row() for index in self.tblDetailedResults.selectedIndexes()): text += "\t".join([ self.tblDetailedResults.item(row, col).text() for col in cols ]) text += '\n' return text def keyPressEvent(self, e): if e.key() == Qt.Key_Copy or e.key == QKeySequence( QKeySequence.Copy) or e.key() == 67: QApplication.clipboard().setText(self.get_selected_cells_as_text()) @pyqtSlot(QPoint) def on_tblDetailedResults_customContextMenuRequested(self, point): """ Handles "Copy" right click selections on the table """ copy_action = QAction('Copy', self) menu = QMenu(self.tblDetailedResults) menu.addAction(copy_action) action = menu.exec_(self.tblDetailedResults.mapToGlobal(point)) if action == copy_action: QApplication.clipboard().setText(self.get_selected_cells_as_text()) def closeSelected(self): """ Closes dialog """ super().accept()
class ManageInfluencesDialog(ui_manage_influences_dialog.Ui_Dialog, QDialog): def __init__(self, parent=None, modify_flag=False): QDialog.__init__(self) self.parent = parent self.modify_flag = modify_flag self.session = Session() self.settings = RaseSettings() self.setupUi(self) self.setInfluencesTable() self.btnDeleteSelectedInfluences.clicked.connect( self.deleteSelectedInfluences) self.buttonExport.clicked.connect(self.handleExport) self.buttonImport.clicked.connect(self.handleImport) self.btnAddNewInfluence.clicked.connect( self.on_btnNewInfluence_clicked) if self.modify_flag: self.setWindowTitle('Modify Influences') else: self.setWindowTitle('Add Influences') self.influencesToDelete = [] def setInfluencesTable(self): influences = list(self.session.query(DetectorInfluence)) if self.tblInfluences: self.tblInfluences.clear() self.tblInfluences.setColumnCount(NUM_COL) self.tblInfluences.setRowCount(len(influences)) self.tblInfluences.setHorizontalHeaderLabels([ 'Name', 'Infl_0\n(offset)', 'Drift\n(Infl_0)', 'Infl_1\n(linear)', 'Drift\n(Infl_1)', 'Infl_2\n(quadratic)', 'Drift\n(Infl_2)', 'Fixed Smear\n(Eres, keV)', 'Drift\n(Fixed Smear)', 'Linear Smear\n(Eres, %)', 'Drift\n(Linear Smear)' ]) self.tblInfluences.setSelectionBehavior(QAbstractItemView.SelectRows) self.tblInfluences.horizontalHeader().setSectionResizeMode( 0, QHeaderView.Stretch) row = 0 if self.modify_flag: union = Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled else: union = Qt.ItemIsSelectable | Qt.ItemIsEnabled for influence in influences: item = QTableWidgetItem(QTableWidgetItem(influence.influence_name)) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tblInfluences.setItem(row, NAME, item) for (col, infl) in zip(COLUMNS[1:], [ str(influence.infl_0), str(influence.degrade_infl0), str(influence.infl_1), str(influence.degrade_infl1), str(influence.infl_2), str(influence.degrade_infl2), str(influence.fixed_smear), str(influence.degrade_f_smear), str(influence.linear_smear), str(influence.degrade_l_smear) ]): item = QTableWidgetItem(infl) item.setFlags(union) self.tblInfluences.setItem(row, col, item) row += 1 self.tblInfluences.setColumnWidth(INFL_2, 80) self.tblInfluences.setColumnWidth(FIX_SMEAR, 90) self.tblInfluences.setColumnWidth(DEGRADE_FIX_SMEAR, 90) self.tblInfluences.setColumnWidth(LIN_SMEAR, 90) self.tblInfluences.setColumnWidth(DEGRADE_LIN_SMEAR, 90) def on_btnNewInfluence_clicked(self, checked): """ Adds new influence row that is fully editable """ row = self.tblInfluences.rowCount() self.tblInfluences.insertRow(row) union = Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled for col in COLUMNS: item = QTableWidgetItem() item.setFlags(union) if col != NAME and col != INFL_1: item.setText('0.0') elif col == INFL_1: item.setText('1.0') self.tblInfluences.setItem(row, col, item) def handleExport(self): """ Exports to CSV """ path = QFileDialog.getSaveFileName(self, 'Save File', self.settings.getDataDirectory(), 'CSV (*.csv)') if path[0]: with open(path[0], mode='w', newline='') as stream: writer = csv.writer(stream) for row in range(self.tblInfluences.rowCount()): rowdata = [] for column in range(self.tblInfluences.columnCount()): item = self.tblInfluences.item(row, column) if item is not None: rowdata.append(item.text()) else: rowdata.append('') writer.writerow(rowdata) def handleImport(self): """ Imports from CSV """ path = QFileDialog.getOpenFileName(self, 'Open File', self.settings.getDataDirectory(), 'CSV(*.csv)') if path[0]: # FIXME: This doesn't check in any way that the format of the file is correct with open(path[0], mode='r') as stream: self.tblInfluences.setRowCount(0) self.tblInfluences.setColumnCount(0) for rowdata in csv.reader(stream): row = self.tblInfluences.rowCount() self.tblInfluences.insertRow(row) self.tblInfluences.setColumnCount(len(rowdata)) for column, data in enumerate(rowdata): item = QTableWidgetItem(data) self.tblInfluences.setItem(row, column, item) self.tblInfluences.setHorizontalHeaderLabels([ 'Name', 'Infl_0\n(offset)', 'Drift\n(Infl_0)', 'Infl_1\n(linear)', 'Drift\n(Infl_1)', 'Infl_2\n(quadratic)', 'Drift\n(Infl_2)', 'Fixed Smear\n(Eres, keV)', 'Drift\n(Fixed Smear)', 'Linear Smear\n(Eres, %)', 'Drift\n(Linear Smear)' ]) def deleteSelectedInfluences(self): """ Deletes Selected Influences """ rows = self.tblInfluences.selectionModel().selectedRows() indices = [] for r in rows: indices.append(r.row()) indices.sort(reverse=True) for index in indices: name = self.tblInfluences.item(index, NAME).text() self.tblInfluences.removeRow(index) influenceDelete = self.session.query(Influence).filter( Influence.name == name).first() if influenceDelete: self.influencesToDelete.append(influenceDelete) @pyqtSlot() def accept(self): """ Closes dialog """ names = [] for row in range(self.tblInfluences.rowCount()): try: for col in COLUMNS[1:]: float(self.tblInfluences.item(row, col).text()) except: return QMessageBox.critical( self, 'Invalid influence value', 'All specified influence values must be numbers') if not self.tblInfluences.item(row, NAME).text(): return QMessageBox.critical(self, 'Unnamed influence', 'Influence must have a name!') names.append(self.tblInfluences.item(row, NAME).text()) if not self.tblInfluences.item(row, INFL_1).text(): self.tblInfluences.item(row, INFL_1).setText('1') elif float(self.tblInfluences.item(row, INFL_1).text()) <= 0: return QMessageBox.critical( self, 'Linear influence factor error', 'Linear scaling factor must be greater than zero! For no linear scaling, set this value to 1.' ) if len(names) != len(set(names)): return QMessageBox.critical( self, 'Repeat influence', 'Influences must each have unique names!') for row in range(self.tblInfluences.rowCount()): influence_name = self.tblInfluences.item(row, NAME).text() influence = self.session.query(Influence).filter_by( name=influence_name).first() if not influence: influence = Influence() influence.name = influence_name self.session.add(influence) det_influence = self.session.query(DetectorInfluence).filter_by( influence_name=influence_name).first() if not det_influence: det_influence = DetectorInfluence() self.session.add(det_influence) det_influence.influence_name = influence_name for col in COLUMNS[INFL_0:]: if not self.tblInfluences.item(row, col).text(): if col == INFL_1: self.tblInfluences.item(row, col).setText('1') else: self.tblInfluences.item(row, col).setText('0') det_influence.infl_0 = float( self.tblInfluences.item(row, INFL_0).text()) det_influence.degrade_infl0 = float( self.tblInfluences.item(row, DEGRAGE_INFL_0).text()) det_influence.infl_1 = float( self.tblInfluences.item(row, INFL_1).text()) det_influence.degrade_infl1 = float( self.tblInfluences.item(row, DEGRAGE_INFL_1).text()) det_influence.infl_2 = float( self.tblInfluences.item(row, INFL_2).text()) det_influence.degrade_infl2 = float( self.tblInfluences.item(row, DEGRAGE_INFL_2).text()) det_influence.fixed_smear = float( self.tblInfluences.item(row, FIX_SMEAR).text()) det_influence.degrade_f_smear = float( self.tblInfluences.item(row, DEGRADE_FIX_SMEAR).text()) det_influence.linear_smear = float( self.tblInfluences.item(row, LIN_SMEAR).text()) det_influence.degrade_l_smear = float( self.tblInfluences.item(row, DEGRADE_LIN_SMEAR).text()) det_influence.influence = influence for influenceDelete in self.influencesToDelete: for scen in self.session.query(Scenario).filter( Scenario.influences.contains(influenceDelete)).all(): if influenceDelete.name in [i.name for i in scen.influences]: scen_hash = Scenario.scenario_hash( scen.acq_time, scen.scen_materials, scen.scen_bckg_materials, [ i for i in scen.influences if i.name != influenceDelete.name ]) if not self.session.query(Scenario).filter_by( id=scen_hash).first(): smats = [ ScenarioMaterial(dose=mat.dose, fd_mode=mat.fd_mode, material=mat.material) for mat in scen.scen_materials ] sback = [ ScenarioBackgroundMaterial(dose=mat.dose, fd_mode=mat.fd_mode, material=mat.material) for mat in scen.scen_bckg_materials ] Scenario(acq_time=scen.acq_time, replication=scen.replication, scen_materials=smats, scen_bckg_materials=sback, influences=[ i for i in scen.influences if i.name != influenceDelete.name ], scenario_groups=scen.scenario_groups) self.session.delete(scen) self.session.delete(influenceDelete) self.session.commit() if not self.modify_flag: for index in sorted( self.tblInfluences.selectionModel().selectedRows()): row = index.row() col = NAME if index.sibling(row, col).data() not in \ [self.parent.listInfluences.item(itemnum).text() for itemnum in range(self.parent.listInfluences.count())]: self.parent.listInfluences.addItem( index.sibling(row, col).data()) return QDialog.accept(self)
class ManageReplaysDialog(ui_manage_replays_dialog.Ui_Dialog, QDialog): def __init__(self, parent=None): QDialog.__init__(self) self.parent = parent self.session = Session() self.settings = RaseSettings() self.setupUi(self) self.setReplaysTable() self.deleteSelectedReplaysButton.clicked.connect( self.deleteSelectedReplays) self.btnOK.clicked.connect(self.oK) self.buttonSave.clicked.connect(self.accept) self.buttonExport.clicked.connect(self.handleExport) self.buttonImport.clicked.connect(self.handleImport) self.addNewReplayButton.clicked.connect(self.on_btnNewReplay_clicked) def setReplaysTable(self): replays = list(self.session.query(Replay)) if self.tblReplays: self.tblReplays.clear() self.tblReplays.setColumnCount(NUM_COL) self.tblReplays.setRowCount(len(replays)) self.tblReplays.setHorizontalHeaderLabels([ 'Name', 'Replay exe', 'Cmd Line', 'Settings', 'n42 Input Template', 'Results Translator Exe', 'Cmd Line', 'Settings' ]) self.tblReplays.setSelectionBehavior(QAbstractItemView.SelectRows) self.tblReplays.horizontalHeader().setSectionResizeMode( 0, QHeaderView.Stretch) row = 0 for replay in replays: self.tblReplays.setItem(row, NAME, QTableWidgetItem(replay.name)) self.tblReplays.setItem(row, REPL_PATH, QTableWidgetItem(replay.exe_path)) if replay.is_cmd_line: self.tblReplays.setItem(row, REPL_IS_CMD_LINE, QTableWidgetItem("Yes")) else: self.tblReplays.setItem(row, REPL_IS_CMD_LINE, QTableWidgetItem("No")) self.tblReplays.setItem(row, REPL_SETTING, QTableWidgetItem(replay.settings)) self.tblReplays.setItem(row, TEMPLATE_PATH, QTableWidgetItem(replay.n42_template_path)) resultsTranslator = self.session.query( ResultsTranslator).filter_by(name=replay.name).first() if resultsTranslator: self.tblReplays.setItem( row, TRANSL_PATH, QTableWidgetItem(resultsTranslator.exe_path)) if resultsTranslator.is_cmd_line: self.tblReplays.setItem(row, TRANSL_IS_CMD_LINE, QTableWidgetItem("Yes")) else: self.tblReplays.setItem(row, TRANSL_IS_CMD_LINE, QTableWidgetItem("No")) self.tblReplays.setItem( row, TRANSL_SETTING, QTableWidgetItem(resultsTranslator.settings)) row = row + 1 def on_btnNewReplay_clicked(self, checked): """ Adds new replay """ dialog = ReplayDialog(self, self.settings) if dialog.exec_(): self.session.add(dialog.replay) self.session.add(dialog.resultsTranslator) self.session.commit() self.setReplaysTable() def handleExport(self): """ Exports to CSV """ path = QFileDialog.getSaveFileName(self, 'Save File', self.settings.getDataDirectory(), 'CSV (*.csv)') if path[0]: with open(path[0], mode='w', newline='') as stream: writer = csv.writer(stream) for row in range(self.tblReplays.rowCount()): rowdata = [] for column in range(self.tblReplays.columnCount()): item = self.tblReplays.item(row, column) if item is not None: rowdata.append(item.text()) else: rowdata.append('') writer.writerow(rowdata) def handleImport(self): """ Imports from CSV """ path = QFileDialog.getOpenFileName(self, 'Open File', self.settings.getDataDirectory(), 'CSV(*.csv)') if path[0]: # FIXME: This doesn't check in any way that the format of the file is correct with open(path[0], mode='r') as stream: self.tblReplays.setRowCount(0) self.tblReplays.setColumnCount(0) for rowdata in csv.reader(stream): row = self.tblReplays.rowCount() self.tblReplays.insertRow(row) self.tblReplays.setColumnCount(len(rowdata)) for column, data in enumerate(rowdata): item = QTableWidgetItem(data) self.tblReplays.setItem(row, column, item) self.tblReplays.setHorizontalHeaderLabels([ 'Name', 'Replay exe', 'Cmd Line', 'Settings', 'n42 Input Template', 'Results Translator Exe', 'Cmd Line', 'Settings' ]) def deleteSelectedReplays(self): """ Deletes Selected Replays """ rows = self.tblReplays.selectionModel().selectedRows() indices = [] for r in rows: indices.append(r.row()) indices.sort(reverse=True) for index in indices: name = self.tblReplays.item(index, 0).text() self.tblReplays.removeRow(index) replayDelete = self.session.query(Replay).filter( Replay.name == name) replayDelete.delete() resTranslatorDelete = self.session.query(ResultsTranslator).filter( ResultsTranslator.name == name) if resTranslatorDelete: resTranslatorDelete.delete() self.session.commit() if self.parent: self.parent.cmbReplay.removeItem( self.parent.cmbReplay.findText(name)) def accept(self): """ Saves changes """ session = Session() for repl in session.query(Replay): replayDelete = self.session.query(Replay).filter( Replay.name == repl.name) replayDelete.delete() for resTrans in session.query(ResultsTranslator): resTranslatorDelete = self.session.query(ResultsTranslator).filter( ResultsTranslator.name == resTrans.name) resTranslatorDelete.delete() for row in range(self.tblReplays.rowCount()): nameItem = self.tblReplays.item(row, NAME) if not nameItem: QMessageBox.critical(self, 'Insufficient Information', 'Must specify a replay tool name') return isReplCmdItem = self.tblReplays.item(row, REPL_IS_CMD_LINE) if not isReplCmdItem: QMessageBox.critical( self, 'Insufficient Information', 'Must specify whether replay tool is command line') return else: if isReplCmdItem.text().lower() == "yes": isReplCmd = True elif isReplCmdItem.text().lower() == "no": isReplCmd = False else: QMessageBox.critical( self, 'Improper Input', ' Replay Cmd Line setting must be Yes or No') return resTranslPathItem = self.tblReplays.item(row, TRANSL_PATH) isResTransCmdItem = self.tblReplays.item(row, TRANSL_IS_CMD_LINE) if len(resTranslPathItem.text()) > 0: if not isResTransCmdItem: QMessageBox.critical( self, 'Insufficient Information', 'If Results Translator path set, must specify whether Results Translator is command line' ) return if isResTransCmdItem.text().lower() == "yes": isResTransCmd = True elif isResTransCmdItem.text().lower() == "no": isResTransCmd = False else: QMessageBox.critical( self, 'Improper Input', ' Results Translator Cmd Line setting must be Yes or No' ) return replay = Replay(name=nameItem.text()) replay.exe_path = self.tblReplays.item(row, REPL_PATH).text() replay.is_cmd_line = isReplCmd if self.tblReplays.item(row, REPL_SETTING): replay.settings = self.tblReplays.item(row, REPL_SETTING).text() if self.tblReplays.item(row, TEMPLATE_PATH): replay.n42_template_path = self.tblReplays.item( row, TEMPLATE_PATH).text() session.add(replay) if len(resTranslPathItem.text()) > 0: resultsTranslator = ResultsTranslator(name=nameItem.text()) resultsTranslator.exe_path = resTranslPathItem.text() resultsTranslator.is_cmd_line = isResTransCmd if self.tblReplays.item(row, TRANSL_SETTING): resultsTranslator.settings = self.tblReplays.item( row, TRANSL_SETTING).text() session.add(resultsTranslator) session.commit() return QDialog.accept(self) def oK(self): """ Closes dialog """ return QDialog.accept(self)
class CorrespondenceTableDialog(ui_correspondence_table_dialog.Ui_dlgCorrTable, QDialog): def __init__(self): QDialog.__init__(self) self.setupUi(self) self.settings = RaseSettings() self.NUM_COLS = 3 self.tableEdited = False self.session = Session() # self.corTable = None # Query the entries in the current default table corTableRows = self.readCorrTableRows() if not corTableRows: self.NUM_ROWS = 0 else: self.NUM_ROWS = corTableRows.count() self.tblCCCLists.setItemDelegate( Delegate(self.tblCCCLists, isotopeCol=0)) self.tblCCCLists.setRowCount(self.NUM_ROWS) self.tblCCCLists.setColumnCount(self.NUM_COLS) self.columnLabels = ['Source', 'Correct ID', 'Allowed ID'] self.tblCCCLists.setHorizontalHeaderLabels(self.columnLabels) self.tblCCCLists.horizontalHeader().setSectionResizeMode( 1, QHeaderView.Stretch) self.tblCCCLists.horizontalHeader().setSectionResizeMode( 2, QHeaderView.Stretch) self.tblCCCLists.setContextMenuPolicy(Qt.CustomContextMenu) self.tblCCCLists.customContextMenuRequested.connect(self.openMenu) self.tblCCCLists.setSortingEnabled(False) # for row in range(self.NUM_ROWS): # for col in range(self.NUM_COLS): # self.tblCCCLists.setItem(row, col, QTableWidgetItem()) if corTableRows: for row, line in enumerate(corTableRows): # if (row >= self.NUM_ROWS): # self.NUM_ROWS = self.NUM_ROWS + 1 # self.tblCCCLists.setRowCount(self.NUM_ROWS) # for col in range(self.NUM_COLS): # self.tblCCCLists.setItem(row, col, QTableWidgetItem()) self.tblCCCLists.setItem(row, 0, QTableWidgetItem(line.isotope)) self.tblCCCLists.setItem(row, 1, QTableWidgetItem(line.corrList1)) self.tblCCCLists.setItem(row, 2, QTableWidgetItem(line.corrList2)) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) # self.pushButton.clicked.connect(self.addLines) self.btnAddRow.clicked.connect(self.addRow) self.populateDefaultComboBox() self.setSaveAsTableName() self.buttonImport.clicked.connect(self.handleImport) self.buttonExport.clicked.connect(self.handleExport) self.buttonApplyDefaultSetting.clicked.connect(self.applySettings) self.btnDeleteSelected.clicked.connect(self.deleteSelected) self.btnClose.clicked.connect(self.closeSelected) def closeSelected(self): """ closes dialog """ super().accept() def deleteSelected(self): """ deletes selected rows """ rows = self.tblCCCLists.selectionModel().selectedRows() indices = [] for r in rows: indices.append(r.row()) indices.sort(reverse=True) for index in indices: self.tblCCCLists.removeRow(index) self.NUM_ROWS = self.NUM_ROWS - len(indices) def setDefaultCorrTable(self, tableName): """ Sets selected table as default """ corrTable = self.session.query(CorrespondenceTable).filter_by( name=tableName).first() corrTable.is_default = True self.session.commit() def applySettings(self): """ Sets selected table as default and loads it for edit """ self.setDefaultCorrTable(self.setDefaultComboBox.currentText()) self.tblCCCLists.setRowCount(0) if not self.readCorrTableRows(): return corTableRows = self.readCorrTableRows() row = 0 if corTableRows: for line in corTableRows: self.NUM_ROWS = row + 1 self.tblCCCLists.setRowCount(self.NUM_ROWS) for col in range(self.NUM_COLS): self.tblCCCLists.setItem(row, col, QTableWidgetItem()) self.tblCCCLists.setItem(row, 0, QTableWidgetItem(line.isotope)) self.tblCCCLists.setItem(row, 1, QTableWidgetItem(line.corrList1)) self.tblCCCLists.setItem(row, 2, QTableWidgetItem(line.corrList2)) row = row + 1 self.setSaveAsTableName() def populateDefaultComboBox(self): """ loads available correspondence tables into the selection box """ corrTables = list(self.session.query(CorrespondenceTable)) for i, table in enumerate(corrTables): self.setDefaultComboBox.addItem(table.name) if table.is_default: self.setDefaultComboBox.setCurrentIndex(i) def setSaveAsTableName(self): corrTable = self.session.query(CorrespondenceTable).filter_by( is_default=True).one_or_none() if corrTable is not None: self.txtCorrespondenceTable.setText(corrTable.name) self.txtCorrespondenceTable.repaint() def readCorrTableRows(self): """ Queries elements of the Default Correspondence Table :return: row elements of the correspondence table from DB """ corrTable = self.session.query(CorrespondenceTable).filter_by( is_default=True).one_or_none() if corrTable is None: return None else: return self.session.query(CorrespondenceTableElement).filter_by( corr_table_name=corrTable.name) def addRow(self): """ adds row to the table in the open dialog """ self.NUM_ROWS = self.NUM_ROWS + 1 self.tblCCCLists.setRowCount(self.NUM_ROWS) for col in range(self.NUM_COLS): self.tblCCCLists.setItem(self.NUM_ROWS - 1, col, QTableWidgetItem()) def accept(self): table_name = self.txtCorrespondenceTable.text() if table_name == "": QMessageBox.information(self, 'Correspondence Table Name Needed', 'Please Specify New Table Name') return corrTable = self.session.query(CorrespondenceTable).filter_by( name=table_name).one_or_none() if corrTable is not None: # table already exists, so delete first before overwriting self.session.query(CorrespondenceTableElement).filter_by( corr_table_name=self.txtCorrespondenceTable.text()).delete() self.session.delete(corrTable) self.session.commit() table = CorrespondenceTable(name=table_name, is_default=True) self.session.add(table) for row in range(self.NUM_ROWS): iso = self.tblCCCLists.item(row, 0).text() l1 = self.tblCCCLists.item(row, 1).text() l2 = self.tblCCCLists.item(row, 2).text() if iso != '': corrTsbleEntry = CorrespondenceTableElement(isotope=iso, corrList1=l1, corrList2=l2) table.corr_table_elements.append(corrTsbleEntry) else: break self.session.commit() super().accept() def handleExport(self): """ exports to CSV """ path = QFileDialog.getSaveFileName(self, 'Save File', self.settings.getDataDirectory(), 'CSV (*.csv)') if path[0]: with open(path[0], mode='w', newline='') as stream: writer = csv.writer(stream) writer.writerow(self.columnLabels) for row in range(self.tblCCCLists.rowCount()): rowdata = [] for column in range(self.tblCCCLists.columnCount()): item = self.tblCCCLists.item(row, column) if item is not None: rowdata.append(item.text()) else: rowdata.append('') writer.writerow(rowdata) def handleImport(self): """ imports from CSV """ path = QFileDialog.getOpenFileName(self, 'Open File', self.settings.getDataDirectory(), 'CSV(*.csv)') if path[0]: # FIXME: This doesn't check in any way that the format of the file is correct with open(path[0], mode='r') as stream: self.tblCCCLists.setRowCount(0) self.tblCCCLists.setColumnCount(0) for rowdata in csv.reader(stream): row = self.tblCCCLists.rowCount() if 'Correct ID' in str(rowdata): continue self.tblCCCLists.insertRow(row) self.tblCCCLists.setColumnCount(len(rowdata)) for column, data in enumerate(rowdata): item = QTableWidgetItem(data) self.tblCCCLists.setItem(row, column, item) self.tblCCCLists.setHorizontalHeaderLabels(self.columnLabels) self.tblCCCLists.horizontalHeader().setSectionResizeMode( 1, QHeaderView.Stretch) self.tblCCCLists.horizontalHeader().setSectionResizeMode( 2, QHeaderView.Stretch) self.NUM_ROWS = self.tblCCCLists.rowCount() @pyqtSlot(QTableWidgetItem) def on_tblCCCLists_itemChanged(self, item): """ Listener for changes to the table """ self.tableEdited = True def openMenu(self, point): """ Adds sorting to the table """ sortAction = QAction('Sort', self) menu = QMenu(self.tblCCCLists) menu.addAction(sortAction) action = menu.exec_(self.tblCCCLists.mapToGlobal(point)) if action == sortAction: sortingCol = self.tblCCCLists.currentColumn() sortingList = [] rowMap = {} for row in range(self.NUM_ROWS): if self.tblCCCLists.item(row, 0).text() == "": continue rowMapItem = {} for col in range(self.NUM_COLS): if col != sortingCol: rowMapItem[col] = self.tblCCCLists.item(row, col).text() rowMap[self.tblCCCLists.item(row, sortingCol).text()] = rowMapItem sortingList.append( self.tblCCCLists.item(row, sortingCol).text()) sortingList.sort() row = 0 for token in sortingList: rowMapItem = rowMap[token] for col in rowMapItem: self.tblCCCLists.setItem(row, col, QTableWidgetItem(rowMapItem[col])) self.tblCCCLists.setItem(row, sortingCol, QTableWidgetItem(token)) row = row + 1
class ManageWeightsDialog(ui_manage_weights_dialog.Ui_Dialog, QDialog): def __init__(self, parent=None): QDialog.__init__(self) self.parent = parent self.session = Session() self.settings = RaseSettings() self.setupUi(self) self.setWeightsTable() self.check_usemweights.setChecked( self.settings.getUseMWeightsInCalcs()) self.check_useconfs.setChecked( self.settings.getUseConfidencesInCalcs()) self.btnDeleteSelectedMaterials.clicked.connect( self.deleteSelectedMaterials) self.buttonExport.clicked.connect(self.handleExport) self.buttonImport.clicked.connect(self.handleImport) self.btnAddNewMaterial.clicked.connect(self.on_btnNewMaterial_clicked) self.setWindowTitle('Modify Material Weights') self.materialsToDelete = [] def setWeightsTable(self): materials = list(self.session.query(MaterialWeight)) if self.tblWeights: self.tblWeights.clear() self.tblWeights.setItemDelegate( Delegate(self.tblWeights, isotopeCol=NAME)) self.tblWeights.setColumnCount(NUM_COL) self.tblWeights.setRowCount(len(materials)) self.tblWeights.setHorizontalHeaderLabels([ 'Material Name', 'True Positive\nWeighting Factor', 'False Positive\nWeighting Factor', 'False Negative\nWeighting Factor' ]) self.tblWeights.setSelectionBehavior(QAbstractItemView.SelectRows) self.tblWeights.horizontalHeader().setSectionResizeMode( 0, QHeaderView.Stretch) row = 0 for col in COLUMNS[TPWF:]: self.tblWeights.setColumnWidth(col, 120) for material in materials: item = QTableWidgetItem(QTableWidgetItem(material.name)) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled) self.tblWeights.setItem(row, NAME, item) for (col, weight) in zip( COLUMNS[TPWF:], [str(material.TPWF), str(material.FPWF), str(material.FNWF)]): item = QTableWidgetItem(weight) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled) self.tblWeights.setItem(row, col, item) row += 1 def on_btnNewMaterial_clicked(self, checked): """ Adds new influence row that is fully editable """ row = self.tblWeights.rowCount() self.tblWeights.insertRow(row) union = Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled for col in COLUMNS: item = QTableWidgetItem() item.setFlags(union) if col > NAME: item.setText('1') self.tblWeights.setItem(row, col, item) def handleExport(self): """ Exports to CSV """ path = QFileDialog.getSaveFileName(self, 'Save File', self.settings.getDataDirectory(), 'CSV (*.csv)') if path[0]: with open(path[0], mode='w', newline='') as stream: writer = csv.writer(stream) for row in range(self.tblWeights.rowCount()): rowdata = [] for column in range(self.tblWeights.columnCount()): item = self.tblWeights.item(row, column) if item is not None: rowdata.append(item.text()) else: rowdata.append('') writer.writerow(rowdata) def handleImport(self): """ Imports from CSV """ path = QFileDialog.getOpenFileName(self, 'Open File', self.settings.getDataDirectory(), 'CSV(*.csv)') if path[0]: # FIXME: This doesn't check in any way that the format of the file is correct with open(path[0], mode='r') as stream: self.tblWeights.setRowCount(0) self.tblWeights.setColumnCount(0) for rowdata in csv.reader(stream): row = self.tblWeights.rowCount() self.tblWeights.insertRow(row) self.tblWeights.setColumnCount(len(rowdata)) for column, data in enumerate(rowdata): item = QTableWidgetItem(data) self.tblWeights.setItem(row, column, item) self.tblWeights.setHorizontalHeaderLabels([ 'Material Name', 'True Positive\nWeighting Factor', 'False Positive\nWeighting Factor', 'False Negative\nWeighting Factor' ]) for col in COLUMNS[TPWF:]: self.tblWeights.setColumnWidth(col, 120) def deleteSelectedMaterials(self): """ Deletes Selected Influences """ rows = self.tblWeights.selectionModel().selectedRows() indices = [] for r in rows: indices.append(r.row()) indices.sort(reverse=True) for index in indices: self.tblWeights.removeRow(index) def removeDeletedWeightsFromDB(self): session = Session() materialsToDelete = [] prior_mats = list(session.query(MaterialWeight).all()) current_mats = list( self.tblWeights.item(row, NAME).text() for row in range(self.tblWeights.rowCount())) for mat in prior_mats: if mat.name not in current_mats: materialsToDelete.append(mat) for materialDelete in materialsToDelete: self.session.delete(materialDelete) @pyqtSlot() def accept(self): """ Closes dialog """ self.settings.setUseConfidencesInCalcs(self.check_useconfs.isChecked()) self.settings.setUseMWeightsInCalcs(self.check_usemweights.isChecked()) self.removeDeletedWeightsFromDB() names = [] for row in range(self.tblWeights.rowCount()): try: for col in COLUMNS[TPWF:]: if not self.tblWeights.item(row, col).text(): self.tblWeights.item(row, col).setText('1') float(self.tblWeights.item(row, col).text()) except: return QMessageBox.critical( self, 'Invalid weight value', 'All specified weight values must be numbers') if not self.tblWeights.item(row, NAME).text(): return QMessageBox.critical( self, 'Unnamed Material', 'Material weights must have a name!') names.append(self.tblWeights.item(row, NAME).text()) if len(names) != len(set(names)): return QMessageBox.critical( self, 'Repeat material', 'Materials must each have unique names!') for row in range(self.tblWeights.rowCount()): name = self.tblWeights.item(row, NAME).text() material = self.session.query(MaterialWeight).filter_by( name=name).first() if not material: material = MaterialWeight() material.name = name self.session.add(material) for col in COLUMNS[TPWF:]: if not self.tblWeights.item(row, col).text(): self.tblWeights.item(row, col).setText('1') elif float(self.tblWeights.item(row, col).text()) <= 0: self.tblWeights.item(row, col).setText('0') material.TPWF = float(self.tblWeights.item(row, TPWF).text()) material.FPWF = float(self.tblWeights.item(row, FPWF).text()) material.FNWF = float(self.tblWeights.item(row, FNWF).text()) self.session.commit() return QDialog.accept(self)
class SettingsDialog(ui_prefs_dialog.Ui_Dialog, QDialog): def __init__(self, parent): QDialog.__init__(self, parent) self.settings = RaseSettings() self.setupUi(self) self.txtDataDir.setReadOnly(True) self.txtDataDir.setText(self.settings.getDataDirectory()) self.dataDirectoryChanged = False self.algoDictionary = {} algoCount = 0 for name, data in inspect.getmembers(sampling_algos, predicate=inspect.isfunction): try: readable_name = data.__doc__.splitlines()[0].strip() if readable_name == '': raise Exception("") except: readable_name = name self.downSapmplingAlgoComboBox.addItem(readable_name) self.algoDictionary[algoCount] = data if data == self.settings.getSamplingAlgo(): self.downSapmplingAlgoComboBox.setCurrentIndex(algoCount) algoCount += 1 self.algorithmSelected = False self.downSapmplingAlgoComboBox.currentIndexChanged.connect( self.chooseSamplingAlgo) @pyqtSlot(bool) def on_btnBrowseDataDir_clicked(self, checked): """ Selects Data Directory """ options = QFileDialog.ShowDirsOnly if sys.platform.startswith('win'): options = QFileDialog.DontUseNativeDialog dir = QFileDialog.getExistingDirectory( self, 'Choose RASE Data Directory', self.settings.getDataDirectory(), options) if dir: self.txtDataDir.setText(dir) self.dataDirectoryChanged = True def chooseSamplingAlgo(self, index): """ Selects Sampling Algo """ self.algorithmSelected = True @pyqtSlot() def accept(self): if self.dataDirectoryChanged: self.settings.setDataDirectory( os.path.normpath(self.txtDataDir.text())) idx = self.downSapmplingAlgoComboBox.currentIndex() if self.algorithmSelected: self.settings.setSamplingAlgo(self.algoDictionary[idx]) # if current state is different from initial state (somehow record the initial state so if the user toggles # but then toggles back the user is not prompted to reset RASE) super().accept()
class ManageReplaysDialog(ui_manage_replays_dialog.Ui_Dialog, QDialog): def __init__(self, parent = None): QDialog.__init__(self) self.parent = parent self.session = Session() self.settings = RaseSettings() self.setupUi(self) self.setReplaysTable() self.deleteSelectedReplaysButton.clicked.connect(self.deleteSelectedReplays) self.btnOK.clicked.connect(self.oK) self.buttonExport.clicked.connect(self.handleExport) self.buttonImport.clicked.connect(self.handleImport) self.addNewReplayButton.clicked.connect(self.on_btnNewReplay_clicked) def setReplaysTable(self): replays = list(self.session.query(Replay)) if self.tblReplays: self.tblReplays.clear() self.tblReplays.setColumnCount(NUM_COL) self.tblReplays.setRowCount(len(replays)) self.tblReplays.setHorizontalHeaderLabels(['Name', 'Replay exe', 'Cmd Line', 'Settings', 'n42 Input Template', 'Results Translator Exe', 'Cmd Line' , 'Settings']) self.tblReplays.setSelectionBehavior(QAbstractItemView.SelectRows) self.tblReplays.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) row = 0 for replay in replays: self.tblReplays.setItem(row, NAME, QTableWidgetItem(replay.name)) self.tblReplays.setItem(row, REPL_PATH, QTableWidgetItem(replay.exe_path)) if replay.is_cmd_line: self.tblReplays.setItem(row, REPL_IS_CMD_LINE, QTableWidgetItem("Yes")) else: self.tblReplays.setItem(row, REPL_IS_CMD_LINE, QTableWidgetItem("No")) self.tblReplays.setItem(row, REPL_SETTING, QTableWidgetItem(replay.settings)) self.tblReplays.setItem(row, TEMPLATE_PATH, QTableWidgetItem(replay.n42_template_path)) resultsTranslator = self.session.query(ResultsTranslator).filter_by(name=replay.name).first() if resultsTranslator: self.tblReplays.setItem(row, TRANSL_PATH, QTableWidgetItem(resultsTranslator.exe_path)) if resultsTranslator.is_cmd_line: self.tblReplays.setItem(row, TRANSL_IS_CMD_LINE, QTableWidgetItem("Yes")) else: self.tblReplays.setItem(row, TRANSL_IS_CMD_LINE, QTableWidgetItem("No")) self.tblReplays.setItem(row, TRANSL_SETTING, QTableWidgetItem(resultsTranslator.settings)) else: self.tblReplays.setItem(row, TRANSL_IS_CMD_LINE, QTableWidgetItem(" ")) self.tblReplays.setItem(row, TRANSL_SETTING, QTableWidgetItem(" ")) row = row + 1 self.tblReplays.setEditTriggers(QAbstractItemView.NoEditTriggers) def on_btnNewReplay_clicked(self, checked): """ Adds new replay """ dialog = ReplayDialog(self) if dialog.exec_(): self.session.add(dialog.replay) self.session.add(dialog.resultsTranslator) self.session.commit() self.setReplaysTable() @pyqtSlot(int, int) def on_tblReplays_cellDoubleClicked(self, row, col): """ Edit existing replay on double clicking a row """ replay = self.session.query(Replay).filter_by( name=self.tblReplays.item(row, NAME).text()).first() resultsTranslator = self.session.query(ResultsTranslator).filter_by( name=self.tblReplays.item(row, NAME).text()).first() if ReplayDialog(self, replay, resultsTranslator).exec_(): self.setReplaysTable() def handleExport(self): """ Exports to CSV """ path = QFileDialog.getSaveFileName(self, 'Save File', self.settings.getDataDirectory(), 'CSV (*.csv)') if path[0]: with open(path[0], mode='w', newline='') as stream: writer = csv.writer(stream) for row in range(self.tblReplays.rowCount()): rowdata = [] for column in range(self.tblReplays.columnCount()): item = self.tblReplays.item(row, column) if item is not None: rowdata.append(item.text()) else: rowdata.append('') writer.writerow(rowdata) def handleImport(self): """ Imports from CSV """ path = QFileDialog.getOpenFileName(self, 'Open File', self.settings.getDataDirectory(), 'CSV(*.csv)') if path[0]: # FIXME: This doesn't check in any way that the format of the file is correct with open(path[0], mode='r') as stream: self.tblReplays.setRowCount(0) self.tblReplays.setColumnCount(0) for rowdata in csv.reader(stream): row = self.tblReplays.rowCount() self.tblReplays.insertRow(row) self.tblReplays.setColumnCount(len(rowdata)) for column, data in enumerate(rowdata): item = QTableWidgetItem(data) self.tblReplays.setItem(row, column, item) self.tblReplays.setHorizontalHeaderLabels( ['Name', 'Replay exe', 'Cmd Line', 'Settings', 'n42 Input Template', 'Results Translator Exe', 'Cmd Line', 'Settings']) def deleteSelectedReplays(self): """ Deletes Selected Replays """ rows = self.tblReplays.selectionModel().selectedRows() indices =[] for r in rows: indices.append(r.row()) indices.sort(reverse=True) for index in indices: name = self.tblReplays.item(index,0).text() self.tblReplays.removeRow(index) replayDelete = self.session.query(Replay).filter(Replay.name == name) replayDelete.delete() resTranslatorDelete = self.session.query(ResultsTranslator).filter(ResultsTranslator.name == name) if resTranslatorDelete: resTranslatorDelete.delete() self.session.commit() if self.parent: self.parent.cmbReplay.removeItem(self.parent.cmbReplay.findText(name)) def oK(self): """ Closes dialog """ return QDialog.accept(self)
class ReplayDialog(ui_new_replay_dialog.Ui_ReplayDialog, QDialog): def __init__(self, parent, replay=None, resultsTranslator=None): QDialog.__init__(self, parent) self.parent = parent self.setupUi(self) self.settings = RaseSettings() self.replay = replay self.resultsTranslator = resultsTranslator if replay: self.setWindowTitle('Edit Replay Software') self.txtName.setReadOnly(True) self.txtName.setText(replay.name) self.txtCmdLine.setText(replay.exe_path) self.txtSettings.setText(replay.settings) self.cbCmdLine.setChecked(bool(replay.is_cmd_line)) self.txtTemplatePath.setText(replay.n42_template_path) self.txtFilenameSuffix.setText(replay.input_filename_suffix) if resultsTranslator: self.txtResultsTranslator.setText(resultsTranslator.exe_path) self.txtSettings_3.setText(resultsTranslator.settings) self.cbCmdLine_3.setChecked(bool(resultsTranslator.is_cmd_line)) @pyqtSlot(bool) def on_btnBrowseExecutable_clicked(self, checked): """ Selects Replay executable """ filepath = QFileDialog.getOpenFileName( self, 'Path to Replay Tool', self.settings.getDataDirectory())[0] if filepath: self.txtCmdLine.setText(filepath) @pyqtSlot(bool) def on_btnBrowseTranslator_clicked(self, checked): """ Selects Translator Template """ filepath = QFileDialog.getOpenFileName( self, 'Path to n42 Template', self.settings.getDataDirectory())[0] if filepath: self.txtTemplatePath.setText(filepath) @pyqtSlot(bool) def on_btnBrowseResultsTranslator_clicked(self, checked): """ Selects Results Translator Executable """ filepath = QFileDialog.getOpenFileName( self, 'Path to Results Translator Tool', self.settings.getDataDirectory())[0] if filepath: self.txtResultsTranslator.setText(filepath) @pyqtSlot() def accept(self): name = self.txtName.text() if not name: QMessageBox.critical(self, 'Insufficient Information', 'Must specify a replay tool name') return session = Session() if not self.replay: repl = session.query(Replay).filter_by(name=name).first() if repl: QMessageBox.warning( self, 'Bad Replay Name', 'Replay with this name exists. Specify Different Replay Name' ) return self.replay = Replay(name=name) self.parent.new_replay = self.replay session.add(self.replay) self.replay.exe_path = self.txtCmdLine.text() self.replay.is_cmd_line = self.cbCmdLine.isChecked() self.replay.settings = self.txtSettings.text().strip() self.replay.n42_template_path = self.txtTemplatePath.text() self.replay.input_filename_suffix = self.txtFilenameSuffix.text() if not self.resultsTranslator: self.resultsTranslator = ResultsTranslator(name=name) session.add(self.resultsTranslator) self.resultsTranslator.exe_path = self.txtResultsTranslator.text() self.resultsTranslator.is_cmd_line = self.cbCmdLine_3.isChecked() self.resultsTranslator.settings = self.txtSettings_3.text().strip() session.commit() return QDialog.accept(self)
class DetailedResultsDialog(ui_detailed_results_dialog.Ui_dlgDetailedResults, QDialog): def __init__(self, resultMap, scenario, detector): QDialog.__init__(self) self.setupUi(self) self.settings = RaseSettings() self.headerLabels = ['File', 'Tp', 'Fn', 'Fp', 'Precision', 'Recall', 'Fscore', 'IDs'] self.NUM_COLS = len(self.headerLabels) self.session = Session() self.scenario = scenario self.detector = detector self.tblDetailedResults.setContextMenuPolicy(Qt.CustomContextMenu) self.tblDetailedResults.setColumnCount(self.NUM_COLS) self.tblDetailedResults.setHorizontalHeaderLabels(self.headerLabels) self.tblDetailedResults.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) self.tblDetailedResults.setRowCount(0) row=0 for file in sorted(resultMap, key=natural_keys): results = resultMap[file] results.insert(0, file) self.tblDetailedResults.insertRow(row) for col in range(self.NUM_COLS): self.tblDetailedResults.setItem(row, col, QTableWidgetItem(results[col])) row = row + 1 results.pop(0) self.tblDetailedResults.resizeColumnsToContents() self.tblDetailedResults.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) self.buttonOK.clicked.connect(self.closeSelected) self.buttonExport.clicked.connect(self.handleExport) def handleExport(self): """ exports to CSV """ path = QFileDialog.getSaveFileName(self, 'Save File', self.settings.getDataDirectory(), 'CSV (*.csv)') if path[0]: with open(path[0], mode='w', newline='') as stream: writer = csv.writer(stream) writer.writerow(self.headerLabels) for row in range(self.tblDetailedResults.rowCount()): rowdata = [] for column in range(self.tblDetailedResults.columnCount()): item = self.tblDetailedResults.item(row, column) if item is not None: rowdata.append(item.text()) else: rowdata.append('') writer.writerow(rowdata) def get_selected_cells_as_text(self): """ Returns the selected cells of the table as plain text """ selected_rows = self.tblDetailedResults.selectedIndexes() text = "" # show the context menu only if on an a valid part of the table if selected_rows: cols = set(index.column() for index in self.tblDetailedResults.selectedIndexes()) for row in set(index.row() for index in self.tblDetailedResults.selectedIndexes()): text += "\t".join([self.tblDetailedResults.item(row, col).text() for col in cols]) text += '\n' return text def keyPressEvent(self, e): if e.key() == Qt.Key_Copy or e.key == QKeySequence(QKeySequence.Copy) or e.key() == 67: QApplication.clipboard().setText(self.get_selected_cells_as_text()) @pyqtSlot(QPoint) def on_tblDetailedResults_customContextMenuRequested(self, point): """ Handles "Copy" right click selections on the table """ copy_action = QAction('Copy', self) menu = QMenu(self.tblDetailedResults) menu.addAction(copy_action) action = menu.exec_(self.tblDetailedResults.mapToGlobal(point)) if action == copy_action: QApplication.clipboard().setText(self.get_selected_cells_as_text()) @pyqtSlot(int, int) def on_tblDetailedResults_cellDoubleClicked(self, row, col): """ Listens for cell double click and launches sample spectra viewer """ # FIXME: Note plotting order is guaranteed only if filenames of results files match sample spectra filenames if count_files_endwith(get_sample_dir(self.settings.getSampleDirectory(), self.detector, self.scenario.id), (".n42",)) > row: SampleSpectraViewerDialog(self, self.scenario, self.detector, row).exec_() else: QMessageBox.information(self, 'Information', 'Unable to display spectra.<br>' 'No sampled spectra available in native RASE format') def closeSelected(self): """ Closes dialog """ super().accept()