def __init__(self, tableData, headers, colors, coefficients): """Initialization code that is executed on every instantiation Parameters ---------- tableData: list A nested list [row][column] of the data that is associated with the QTableViewWidget. This data should always be up to date with what is present in the table view itself. Passed in to the PalettedTableModel headers: list Column header names passed in as a list of strings. Passed in to the PalettedTableModel colors: list List of strings that include hex colors defining the table row coloring. Passed in to the PalettedTableModel """ super(StartupWindow, self).__init__() self.setupUi(self) # Center the window by default screenCenter = QApplication.desktop().screen().rect().center() windowCenter = self.rect().center() self.move(screenCenter - windowCenter) # Set the icon self.setWindowIcon(QIcon('hand_drill.png')) # Set up the propoerties dictating probe layout self.schlumbergerLayout = False self.wennerLayout = False self.colors = colors self.color = colors[0] # Set up the coefficients (self.shortFilterCoefficients, self.longFilterCoefficients, self.wennerCoefficients) = coefficients # Set up the model and tableView self.model = PalettedTableModel(tableData, headers, colors) self.initTableView(self.model) self.tableViewWidget.resizeColumnsToContents() self.tableViewWidget.show() self.aggregateTableForPlot() # Set up the QWidget with an InteractiveCanvas and # NavigationToolbar instance self.canvas = InteractiveCanvas( np.array([]), np.array([]), xlabel='Voltage Probe Spacing (m)', ylabel='Resistivity (Ohm-m)', linestyle='--', marker='o', dpi=150, hold=False, alpha=0.4, colors=self.colors) self.canvas.setParent(self) self.toolbar = NavigationToolbar( self.canvas, self.canvas, coordinates=True) self.mplvl.addWidget(self.canvas) self.mplvl.addWidget(self.toolbar) # Connect to the canvas to handle mouse clicks self.canvas.mpl_connect( 'button_press_event', self.canvas.onPress) self.canvas.mpl_connect( 'button_release_event', self.canvas.onRelease) # Connect to table buttons self.addRowButton.clicked.connect(self.addRow) self.removeRowButton.clicked.connect(self.removeRow) # Connect to spacing radio buttons self.wennerRadioButton.toggled.connect(self.wenner) self.schlumbergerRadioButton.toggled.connect(self.schlumberger) # Connect to plot buttons self.plotDataButton.clicked.connect(self.compute) self.newRectangleButton.clicked.connect(self.newRectangle) self.resetPlotButton.clicked.connect(self.resetPlot) # Connect to the decimal separator check box self.decimalCheckBox.clicked.connect(self.model.stripCommas) # self.initUi() # Connect to analysis button self.launchAnalysisButton.clicked.connect(self.launchReportWindow) self.initUi()
class StartupWindow(QStartupWindow, UI_StartupWindow): """A Qt QMainWindow class object that is the first window of the Water 4/DRI VES Inverse Analysis software """ def __init__(self, tableData, headers, colors, coefficients): """Initialization code that is executed on every instantiation Parameters ---------- tableData: list A nested list [row][column] of the data that is associated with the QTableViewWidget. This data should always be up to date with what is present in the table view itself. Passed in to the PalettedTableModel headers: list Column header names passed in as a list of strings. Passed in to the PalettedTableModel colors: list List of strings that include hex colors defining the table row coloring. Passed in to the PalettedTableModel """ super(StartupWindow, self).__init__() self.setupUi(self) # Center the window by default screenCenter = QApplication.desktop().screen().rect().center() windowCenter = self.rect().center() self.move(screenCenter - windowCenter) # Set the icon self.setWindowIcon(QIcon('hand_drill.png')) # Set up the propoerties dictating probe layout self.schlumbergerLayout = False self.wennerLayout = False self.colors = colors self.color = colors[0] # Set up the coefficients (self.shortFilterCoefficients, self.longFilterCoefficients, self.wennerCoefficients) = coefficients # Set up the model and tableView self.model = PalettedTableModel(tableData, headers, colors) self.initTableView(self.model) self.tableViewWidget.resizeColumnsToContents() self.tableViewWidget.show() self.aggregateTableForPlot() # Set up the QWidget with an InteractiveCanvas and # NavigationToolbar instance self.canvas = InteractiveCanvas( np.array([]), np.array([]), xlabel='Voltage Probe Spacing (m)', ylabel='Resistivity (Ohm-m)', linestyle='--', marker='o', dpi=150, hold=False, alpha=0.4, colors=self.colors) self.canvas.setParent(self) self.toolbar = NavigationToolbar( self.canvas, self.canvas, coordinates=True) self.mplvl.addWidget(self.canvas) self.mplvl.addWidget(self.toolbar) # Connect to the canvas to handle mouse clicks self.canvas.mpl_connect( 'button_press_event', self.canvas.onPress) self.canvas.mpl_connect( 'button_release_event', self.canvas.onRelease) # Connect to table buttons self.addRowButton.clicked.connect(self.addRow) self.removeRowButton.clicked.connect(self.removeRow) # Connect to spacing radio buttons self.wennerRadioButton.toggled.connect(self.wenner) self.schlumbergerRadioButton.toggled.connect(self.schlumberger) # Connect to plot buttons self.plotDataButton.clicked.connect(self.compute) self.newRectangleButton.clicked.connect(self.newRectangle) self.resetPlotButton.clicked.connect(self.resetPlot) # Connect to the decimal separator check box self.decimalCheckBox.clicked.connect(self.model.stripCommas) # self.initUi() # Connect to analysis button self.launchAnalysisButton.clicked.connect(self.launchReportWindow) self.initUi() def launchReportWindow(self): """Launches the ReportWindow class on launch of VES Inverse Analysis""" try: self.ReportCanvas = ReportCanvas( self.samplePoints, self.filteredResistivity, self.voltageSpacing, self.apparentResistivity, self.voltageSpacingExtrapolated, self.newResistivity) self.close() self.ReportWindow = ReportWindow(self.ReportCanvas) self.ReportWindow.show except AttributeError: return def initUi(self): """Work in progress. Should hook up file menu""" saveAction = QAction(QIcon('save.png'), '&Save As', self) saveAction.setShortcut('Ctrl+S') saveAction.triggered.connect(QApplication.saveStateRequest) exitAction = QAction(QIcon('exit.png'), '&Exit', self) exitAction.setShortcut('Ctrl+Q') exitAction.triggered.connect(QApplication.quit) # fileMenu = menubar.addMenu('&File') self.menuBar.addAction(saveAction) self.menuBar.addAction(exitAction) # addRowButton = QPushButton() # addRowButton.clicked.connect(PalettedTableModel.insertRows(0, 1)) # self.show() def initTableView(self, model): """Set up the QTableViewWidget with the proper data and row spacing Parameters ---------- model: `PalettedTableModel` An instantiated an active PalettedTableModel instance Notes ----- Run once on instantiation of the class to space the rows """ nRows = len(self.model.table) self.tableViewWidget.setModel(model) # Set the table to span 4 rows in the spacing columns for row in range(0, nRows, 4): for col in columns: self.tableViewWidget.setSpan(row, col, 4, 1) self.tableViewWidget.resizeColumnsToContents() self.tableViewWidget.show() def aggregateTableForPlot(self): """Apply the aggregation function and assign the outputs to the class Notes ----- This method assures that the voltageSpacing, meanVoltage, and meanCurrent properties are up to date with the table """ voltageSpacing, meanVoltage, meanCurrent = aggregateTable( self.model.table) self.voltageSpacing = voltageSpacing self.meanVoltage = meanVoltage self.meanCurrent = meanCurrent def addRow(self): """Add 4 new rows (one sample location) to the table""" startRow = len(self.model.table) self.model.insertRows(startRow, 4) self.initTableView(self.model) def removeRow(self): """Remove 4 rows (one sample location) from the table""" startRow = len(self.model.table) - 4 self.model.removeRows(startRow, 4) self.initTableView(self.model) def initPlot(self, draw=True): """Initialize the plot for the main window Parameters ---------- draw: bool If True, the figure canvas will update. Otherwise, it will not Notes ----- The plot is only created if apparentResistivity has already been calculated """ plt.clf() self.aggregateTableForPlot() if hasattr(self, 'apparentResistivity'): voltageSpacing = self.voltageSpacing # if self.schlumberger: # voltageSpacing = self.voltageSpacing[:-1] self.canvas.initFigure(voltageSpacing, self.apparentResistivity) if draw: self.canvas.fig.tight_layout() self.canvas.draw() def resetPlot(self): """Clear the plot of data and rectangles to redraw the layers""" # Catch ValueError that occurs if reset is clicked prior to plotting try: self.canvas.mplRectangles = [] self.canvas.rectCoordinates = [] plt.clf() self.aggregateTableForPlot() self.initPlot() except ValueError: pass def stripCommas(self, table): """Work in progress. Worth it?""" pass # for row in len(table): # for column in len(table[0]): # value = self.table[row][column] # value.replace(',', '.') # self.table[row][column] = value # del value def newRectangle(self): """Add a new rectangle to the figure canvas Notes ----- Adds the rectangles coordinates to the mplRectangles list and locks the latest rectangle into the plot. This button MUST be pressed prior to launching the analysis to include all defined layers. """ try: if self.canvas.rectxy: self.canvas.mplRectangles.append(self.rect) self.canvas.rectCoordinates.append(self.canvas.rectxy) self.initPlot(draw=False) self.canvas.addPointsAndLine( self.voltageSpacing, self.apparentResistivity, draw=False) if self.canvas.mplRectangles: self.canvas.drawRectangles() plt.tight_layout() self.canvas.ax.figure.canvas.draw() return except AttributeError: pass def compute(self, suppress=False): """Compute apparent resistivity Parameters ---------- suppress: bool If True, message boxes should be suppressed. Implemented to allow cross platform testing between Mac OS X and Ubuntu. Not currently working, likely going to ditch it Notes ----- This is where all of the numerical crunching happens. The radioboxes in the main window define whether the schlumberger or wenner equation is employed, and there are warnings when the data do not fit the assumptions of the model. That is, when all of the spacings are not equal in a Wenner array, there is a warning. When at least 2 of the Schlumberger spacings are equal, there is a warning. Finally, if the operator forgets to select a radio button, there is a warning. """ # Suppress msgBox if this module is not called directly for testing if __name__ != '__main__': suppress = True self.apparentResistivity = None self.aggregateTableForPlot() # Calculate apparent resistivity using the Wenner array if self.wennerLayout == True: # Test and let user know spacing does not indicate Wenner Array if not np.all( self.voltageSpacing * 2 == self.voltageSpacing[0] * 2): self.wennerMessageBox(suppress) self.apparentResistivity = wennerResistivity( self.voltageSpacing, self.meanVoltage, self.meanCurrent) self.canvas.addPointsAndLine( self.voltageSpacing, self.apparentResistivity) voltageSpacingExtrapolated, newResistivity = interpolateFieldData( self.voltageSpacing, self.apparentResistivity, 'wenner') self.voltageSpacingExtrapolated = voltageSpacingExtrapolated self.newResistivity = newResistivity self.filteredResistivity = applyFilter( self.newResistivity, self.wennerCoefficients) sampleInterval = np.log(10) / 3. self.samplePoints = np.arange( start=(-sampleInterval * 2), stop=(sampleInterval * 20), step=sampleInterval) # Calculate apparent resistivity using the Schlumberger array elif self.schlumbergerLayout == True: # Test and let user know spacing does not indicate Schlum. array if np.any(self.voltageSpacing[1:] == self.voltageSpacing[0]): self.schlumbergerMessageBox(suppress) self.apparentResistivity = schlumbergerResistivityModified( self.voltageSpacing, self.meanVoltage, self.meanCurrent) self.canvas.addPointsAndLine( self.voltageSpacing, self.apparentResistivity) voltageSpacingExtrapolated, newResistivity = interpolateFieldData( self.voltageSpacing, self.apparentResistivity, 'schlumberger') self.voltageSpacingExtrapolated = voltageSpacingExtrapolated self.newResistivity = newResistivity self.filteredResistivity = applyFilter( self.newResistivity, self.longFilterCoefficients) sampleInterval = np.log(10) / 3. self.samplePoints = np.arange( start=(-sampleInterval * 2), stop=(sampleInterval * 20), step=sampleInterval) # Provide a message box if neither Wenner nor Schlumberger are selected else: self.noSpacingMessageBox(suppress) return return self.apparentResistivity def wennerMessageBox(self, suppress=False): """Warning message box if Wenner assumptions are not met Parameters ---------- suppress: bool If True, message boxes should be suppressed. Implemented to allow cross platform testing between Mac OS X and Ubuntu. Not currently working, likely going to ditch it """ # Suppress argument is to simplify testing if suppress: return self.msgBox = None self.msgBox = QMessageBox(self) self.msgBox.about( self, 'Warning', ('The probe spacing radio button has been set to Wenner Spacing ' + 'all of the Voltage Sep. values are NOT EQUAL. All Voltage ' + 'Sep. values SHOULD BE EQUAL to eachother.\n\n' + 'Please ensure that the proper radio box is ' + 'selected and that the electrodes are placed in ' + 'the desired arrangement.')) def schlumbergerMessageBox(self, suppress=False): """Warning message box if Schlumberger assumptions are not met Parameters ---------- suppress: bool If True, message boxes should be suppressed. Implemented to allow cross platform testing between Mac OS X and Ubuntu. Not currently working, likely going to ditch it """ # Suppress argument is to simplify testing if suppress: return self.msgBox = None self.msgBox = QMessageBox(self) self.msgBox.about( self, 'Warning', ('The probe spacing radio button has been set to ' + 'Schlumberger Spacing and the first and last ' + 'Voltage Sep. values are EQUAL. The voltage ' + 'separation must follow a particular pattern, ' + 'in which the first and last separation values ' + 'should NOT BE EQUAL.\n\n' + 'Please ensure that the proper radio box is ' + 'selected and that the electrodes are placed in ' + 'the desired arrangement.')) def noSpacingMessageBox(self, suppress=False): """Warning message box if no probe layout radiobutton has been selected Parameters ---------- suppress: bool If True, message boxes should be suppressed. Implemented to allow cross platform testing between Mac OS X and Ubuntu. Not currently working, likely going to ditch it """ # Suppress argument is to simplify testing if suppress: return self.msgBox = None self.msgBox = QMessageBox(self) self.msgBox.about( self, 'Warning', ('The probe spacing radio button has not been set.\n\n' + 'Please indicate whether a Schlumberger or Wenner layout ' 'has been used by selecting one of the radio buttons. The ' + 'radio buttons are located at the botton left of the ' + 'program, near the input table.')) def wenner(self): """Define Wenner layout is True""" self.schlumbergerLayout = False self.wennerLayout = True def schlumberger(self): """Define Schlumberger layout is True""" self.schlumbergerLayout = True self.wennerLayout = False