Exemple #1
0
    def __init__(self, pyweed, parent=None):
        super(PreferencesDialog, self).__init__(parent=parent)
        self.setupUi(self)

        self.pyweed = pyweed

        # Ordered list of all available data centers
        dcs = sorted(URL_MAPPINGS.keys())

        self.eventDataCenterAdapter = ComboBoxAdapter(
            self.eventDataCenterComboBox,
            [(dc, URL_MAPPINGS[dc]) for dc in dcs])

        self.stationDataCenterAdapter = ComboBoxAdapter(
            self.stationDataCenterComboBox,
            [(dc, URL_MAPPINGS[dc]) for dc in dcs])

        self.okButton.pressed.connect(self.accept)
        self.cancelButton.pressed.connect(self.reject)
Exemple #2
0
    def __init__(self, secondsBeforeSpinBox, secondsAfterSpinBox,
                 secondsBeforePhaseComboBox, secondsAfterPhaseComboBox):
        super(TimeWindowAdapter, self).__init__()

        self.timeWindow = TimeWindow()
        # Phase options
        phaseOptions = [(phase.name, phase.label) for phase in PHASES]

        self.secondsBeforeSpinBox = secondsBeforeSpinBox
        self.secondsAfterSpinBox = secondsAfterSpinBox
        self.secondsBeforePhaseAdapter = ComboBoxAdapter(
            secondsBeforePhaseComboBox, phaseOptions)
        self.secondsAfterPhaseAdapter = ComboBoxAdapter(
            secondsAfterPhaseComboBox, phaseOptions)

        # Connect input signals
        self.secondsBeforeSpinBox.valueChanged.connect(self.onTimeWindowChanged)
        self.secondsAfterSpinBox.valueChanged.connect(self.onTimeWindowChanged)
        self.secondsBeforePhaseAdapter.changed.connect(self.onTimeWindowChanged)
        self.secondsAfterPhaseAdapter.changed.connect(self.onTimeWindowChanged)
Exemple #3
0
class TimeWindowAdapter(QtCore.QObject):
    """
    Adapter tying a set of inputs to a TimeWindow
    """

    # Signal indicating that the time window has changed
    changed = QtCore.pyqtSignal()

    def __init__(self, secondsBeforeSpinBox, secondsAfterSpinBox,
                 secondsBeforePhaseComboBox, secondsAfterPhaseComboBox):
        super(TimeWindowAdapter, self).__init__()

        self.timeWindow = TimeWindow()
        # Phase options
        phaseOptions = [(phase.name, phase.label) for phase in PHASES]

        self.secondsBeforeSpinBox = secondsBeforeSpinBox
        self.secondsAfterSpinBox = secondsAfterSpinBox
        self.secondsBeforePhaseAdapter = ComboBoxAdapter(
            secondsBeforePhaseComboBox, phaseOptions)
        self.secondsAfterPhaseAdapter = ComboBoxAdapter(
            secondsAfterPhaseComboBox, phaseOptions)

        # Connect input signals
        self.secondsBeforeSpinBox.valueChanged.connect(self.onTimeWindowChanged)
        self.secondsAfterSpinBox.valueChanged.connect(self.onTimeWindowChanged)
        self.secondsBeforePhaseAdapter.changed.connect(self.onTimeWindowChanged)
        self.secondsAfterPhaseAdapter.changed.connect(self.onTimeWindowChanged)

    def onTimeWindowChanged(self):
        """
        When an input changes, update the time window and emit a notification
        """
        self.timeWindow.update(
            self.secondsBeforeSpinBox.value(),
            self.secondsAfterSpinBox.value(),
            self.secondsBeforePhaseAdapter.getValue(),
            self.secondsAfterPhaseAdapter.getValue()
        )
        self.changed.emit()

    def setValues(self, start_offset, end_offset, start_phase, end_phase):
        """
        This should be called on startup to set the initial values
        """
        self.timeWindow.update(
            start_offset, end_offset, start_phase, end_phase
        )
        self.updateInputs()

    def updateInputs(self):
        """
        Update the inputs to reflect the current state
        """
        self.secondsBeforeSpinBox.setValue(self.timeWindow.start_offset)
        self.secondsAfterSpinBox.setValue(self.timeWindow.end_offset)
        self.secondsBeforePhaseAdapter.setValue(self.timeWindow.start_phase)
        self.secondsAfterPhaseAdapter.setValue(self.timeWindow.end_phase)
Exemple #4
0
class PreferencesDialog(QtWidgets.QDialog,
                        PreferencesDialog.Ui_PreferencesDialog):
    """
    Dialog window for editing preferences.
    """
    def __init__(self, pyweed, parent=None):
        super(PreferencesDialog, self).__init__(parent=parent)
        self.setupUi(self)

        self.pyweed = pyweed

        # Ordered list of all available data centers
        dcs = sorted(URL_MAPPINGS.keys())

        self.eventDataCenterAdapter = ComboBoxAdapter(
            self.eventDataCenterComboBox,
            [(dc, URL_MAPPINGS[dc]) for dc in dcs])

        self.stationDataCenterAdapter = ComboBoxAdapter(
            self.stationDataCenterComboBox,
            [(dc, URL_MAPPINGS[dc]) for dc in dcs])

        self.okButton.pressed.connect(self.accept)
        self.cancelButton.pressed.connect(self.reject)

    def showEvent(self, *args, **kwargs):
        """
        Perform any necessary initialization each time the dialog is opened
        """
        super(PreferencesDialog, self).showEvent(*args, **kwargs)
        # Indicate the currently selected data centers
        self.eventDataCenterAdapter.setValue(self.pyweed.event_data_center)
        self.stationDataCenterAdapter.setValue(self.pyweed.station_data_center)
        self.cacheSizeSpinBox.setValue(
            safe_int(self.pyweed.preferences.Waveforms.cacheSize, 10))

    def accept(self):
        """
        Validate and update the client
        """
        try:
            self.pyweed.set_event_data_center(
                self.eventDataCenterAdapter.getValue())
            self.pyweed.set_station_data_center(
                self.stationDataCenterAdapter.getValue())
        except Exception as e:
            # Error usually means that the user selected a data center that doesn't provide the given service
            QtWidgets.QMessageBox.critical(self,
                                           "Unable to update data center",
                                           str(e))
            # Don't call super() which would close the preferences dialog
            return

        self.pyweed.preferences.Waveforms.cacheSize = self.cacheSizeSpinBox.value(
        )

        return super(PreferencesDialog, self).accept()
Exemple #5
0
    def __init__(self, pyweed, parent=None):
        super(WaveformDialog, self).__init__(parent=parent)

        LOGGER.debug('Initializing waveform dialog...')

        self.setupUi(self)
        self.setWindowTitle('Waveforms')

        # Keep a reference to globally shared components
        self.pyweed = pyweed

        # Time window for waveform selection
        self.timeWindowAdapter = TimeWindowAdapter(
            self.secondsBeforeSpinBox, self.secondsAfterSpinBox,
            self.secondsBeforePhaseComboBox, self.secondsAfterPhaseComboBox)

        self.saveFormatAdapter = ComboBoxAdapter(self.saveFormatComboBox,
                                                 [(f.value, f.label)
                                                  for f in OUTPUT_FORMATS])

        # Initialize any preference-based settings
        self.loadPreferences()

        # Modify default GUI settings
        self.saveDirectoryPushButton.setText(self.waveformDirectory)
        self.saveDirectoryPushButton.setFocusPolicy(QtCore.Qt.NoFocus)

        # Waveforms
        self.waveforms_handler = WaveformsHandler(LOGGER, pyweed.preferences,
                                                  pyweed.station_client)
        # The callbacks here are expensive, so use QueuedConnection to run them asynchronously
        self.waveforms_handler.progress.connect(self.onWaveformDownloaded,
                                                QtCore.Qt.QueuedConnection)
        self.waveforms_handler.done.connect(self.onAllDownloaded,
                                            QtCore.Qt.QueuedConnection)

        # Spinner overlays for downloading and saving
        self.downloadSpinner = SpinnerWidget("Downloading...",
                                             parent=self.downloadGroupBox)
        self.downloadSpinner.cancelled.connect(self.onDownloadCancel)
        self.saveSpinner = SpinnerWidget("Saving...",
                                         cancellable=False,
                                         parent=self.saveGroupBox)

        # Connect signals associated with the main table
        # self.selectionTable.horizontalHeader().sortIndicatorChanged.connect(self.selectionTable.resizeRowsToContents)
        self.selectionTable.itemClicked.connect(self.handleTableItemClicked)

        # Connect the Download and Save GUI elements
        self.downloadPushButton.clicked.connect(self.onDownloadPushButton)
        self.savePushButton.clicked.connect(self.onSavePushButton)
        self.saveDirectoryPushButton.clicked.connect(self.getWaveformDirectory)
        self.saveDirectoryBrowseToolButton.clicked.connect(
            self.browseWaveformDirectory)

        self.saveFormatAdapter.changed.connect(self.resetSave)

        # Connect signals associated with comboBoxes
        # NOTE:  http://www.tutorialspoint.com/pyqt/pyqt_qcombobox_widget.htm
        # NOTE:  currentIndexChanged() responds to both user and programmatic changes.
        #        Use activated() for user initiated changes
        self.eventComboBox.activated.connect(self.onFilterChanged)
        self.networkComboBox.activated.connect(self.onFilterChanged)
        self.stationComboBox.activated.connect(self.onFilterChanged)

        # Connect the timewindow signals
        self.timeWindowAdapter.changed.connect(self.resetDownload)

        # Dictionary of filter values
        self.filters = {}

        # Information about download progress
        self.downloadCount = 0
        self.downloadCompleted = 0

        LOGGER.debug('Finished initializing waveform dialog')
Exemple #6
0
class WaveformDialog(BaseDialog, WaveformDialog.Ui_WaveformDialog):

    tableItems = None
    """
    Dialog window for selection and display of waveforms.
    """
    def __init__(self, pyweed, parent=None):
        super(WaveformDialog, self).__init__(parent=parent)

        LOGGER.debug('Initializing waveform dialog...')

        self.setupUi(self)
        self.setWindowTitle('Waveforms')

        # Keep a reference to globally shared components
        self.pyweed = pyweed

        # Time window for waveform selection
        self.timeWindowAdapter = TimeWindowAdapter(
            self.secondsBeforeSpinBox, self.secondsAfterSpinBox,
            self.secondsBeforePhaseComboBox, self.secondsAfterPhaseComboBox)

        self.saveFormatAdapter = ComboBoxAdapter(self.saveFormatComboBox,
                                                 [(f.value, f.label)
                                                  for f in OUTPUT_FORMATS])

        # Initialize any preference-based settings
        self.loadPreferences()

        # Modify default GUI settings
        self.saveDirectoryPushButton.setText(self.waveformDirectory)
        self.saveDirectoryPushButton.setFocusPolicy(QtCore.Qt.NoFocus)

        # Waveforms
        self.waveforms_handler = WaveformsHandler(LOGGER, pyweed.preferences,
                                                  pyweed.station_client)
        # The callbacks here are expensive, so use QueuedConnection to run them asynchronously
        self.waveforms_handler.progress.connect(self.onWaveformDownloaded,
                                                QtCore.Qt.QueuedConnection)
        self.waveforms_handler.done.connect(self.onAllDownloaded,
                                            QtCore.Qt.QueuedConnection)

        # Spinner overlays for downloading and saving
        self.downloadSpinner = SpinnerWidget("Downloading...",
                                             parent=self.downloadGroupBox)
        self.downloadSpinner.cancelled.connect(self.onDownloadCancel)
        self.saveSpinner = SpinnerWidget("Saving...",
                                         cancellable=False,
                                         parent=self.saveGroupBox)

        # Connect signals associated with the main table
        # self.selectionTable.horizontalHeader().sortIndicatorChanged.connect(self.selectionTable.resizeRowsToContents)
        self.selectionTable.itemClicked.connect(self.handleTableItemClicked)

        # Connect the Download and Save GUI elements
        self.downloadPushButton.clicked.connect(self.onDownloadPushButton)
        self.savePushButton.clicked.connect(self.onSavePushButton)
        self.saveDirectoryPushButton.clicked.connect(self.getWaveformDirectory)
        self.saveDirectoryBrowseToolButton.clicked.connect(
            self.browseWaveformDirectory)

        self.saveFormatAdapter.changed.connect(self.resetSave)

        # Connect signals associated with comboBoxes
        # NOTE:  http://www.tutorialspoint.com/pyqt/pyqt_qcombobox_widget.htm
        # NOTE:  currentIndexChanged() responds to both user and programmatic changes.
        #        Use activated() for user initiated changes
        self.eventComboBox.activated.connect(self.onFilterChanged)
        self.networkComboBox.activated.connect(self.onFilterChanged)
        self.stationComboBox.activated.connect(self.onFilterChanged)

        # Connect the timewindow signals
        self.timeWindowAdapter.changed.connect(self.resetDownload)

        # Dictionary of filter values
        self.filters = {}

        # Information about download progress
        self.downloadCount = 0
        self.downloadCompleted = 0

        LOGGER.debug('Finished initializing waveform dialog')

    # NOTE:  http://stackoverflow.com/questions/12366521/pyqt-checkbox-in-qtablewidget
    # NOTE:  http://stackoverflow.com/questions/30462078/using-a-checkbox-in-pyqt
    @QtCore.pyqtSlot(QtWidgets.QTableWidgetItem)
    def handleTableItemClicked(self, item):
        """
        Triggered whenever an item in the waveforms table is clicked.
        """
        row = item.row()

        LOGGER.debug("Clicked on table row")

        # Toggle the Keep state
        waveformID = str(
            self.selectionTable.item(row, WAVEFORM_ID_COLUMN).text())
        waveform = self.waveforms_handler.get_waveform(waveformID)
        waveform.keep = not waveform.keep
        keepItem = self.selectionTable.item(row, WAVEFORM_KEEP_COLUMN)
        keepItem.setKeep(waveform.keep)

    @QtCore.pyqtSlot()
    def loadWaveformChoices(self):
        """
        Fill the selectionTable with all SNCL-Event combinations selected in the MainWindow.
        This function is triggered whenever the "Get Waveforms" button in the MainWindow is clicked.
        """

        LOGGER.debug('Loading waveform choices...')

        self.resetDownload()

        self.waveforms_handler.create_waveforms(self.pyweed)

        # Add events to the eventComboBox -------------------------------

        self.eventComboBox.clear()

        self.eventComboBox.addItem('All events')
        for event in self.pyweed.iter_selected_events():
            self.eventComboBox.addItem(get_event_name(event))

        # Add networks/stations to the networkComboBox and stationsComboBox ---------------------------

        self.networkComboBox.clear()
        self.networkComboBox.addItem('All networks')

        self.stationComboBox.clear()
        self.stationComboBox.addItem('All stations')

        foundNetworks = set()
        foundStations = set()
        for (network, station,
             _channel) in self.pyweed.iter_selected_stations():
            if network.code not in foundNetworks:
                foundNetworks.add(network.code)
                self.networkComboBox.addItem(network.code)
            netstaCode = '.'.join((network.code, station.code))
            if netstaCode not in foundStations:
                foundStations.add(netstaCode)
                self.stationComboBox.addItem(netstaCode)

        self.loadSelectionTable()

        # Start downloading data
        self.downloadWaveformData()

        LOGGER.debug('Finished loading waveform choices')

    @QtCore.pyqtSlot()
    def loadSelectionTable(self):
        """
        Add event-SNCL combinations to the selection table
        """

        LOGGER.debug('Loading waveform selection table...')

        # Use WaveformTableItems to put the data into the table
        if not self.tableItems:
            self.tableItems = WaveformTableItems(self.selectionTable)
        self.tableItems.fill(self.iterWaveforms())

        self.filterSelectionTable()

        LOGGER.debug('Finished loading waveform selection table')

    @QtCore.pyqtSlot(int)
    def onFilterChanged(self):
        self.filters = {}
        self.filterSelectionTable()

    def filterSelectionTable(self):
        """
        Filter the selection table based on the currently defined filters
        """
        if self.tableItems:

            if not self.filters:
                self.filters = {
                    'event': self.eventComboBox.currentText(),
                    'network': self.networkComboBox.currentText(),
                    'station': self.stationComboBox.currentText(),
                }

            filterResults = dict(
                (waveform.waveform_id, self.applyFilter(waveform))
                for waveform in self.iterWaveforms())

            def filterFn(row):
                waveformID = str(
                    self.selectionTable.item(row, WAVEFORM_ID_COLUMN).text())
                return filterResults.get(waveformID)

            self.tableItems.filter(filterFn)

    def iterWaveforms(self, saveable_only=False):
        """
        Iterate through the waveforms, optionally yielding only the saveable ones
        """
        for waveform in self.waveforms_handler.waveforms:
            if saveable_only and not (waveform.keep and waveform.mseed_exists):
                continue
            yield waveform

    def applyFilter(self, waveform):
        """
        Apply self.filters to the given waveform
        @return True iff the waveform should be included
        """
        # Get the values from the waveform to match against the filter value
        sncl_parts = waveform.sncl.split('.')
        net_code = sncl_parts[0]
        netsta_code = '.'.join(sncl_parts[:2])
        filter_values = {
            'event': get_event_name(waveform.event_ref()),
            'network': net_code,
            'station': netsta_code
        }
        for (fname, fval) in self.filters.items():
            if not fval.startswith('All') and fval != filter_values[fname]:
                return False
        return True

    @QtCore.pyqtSlot()
    def onDownloadPushButton(self):
        """
        Triggered when downloadPushButton is clicked.
        """
        if self.waveformsDownloadStatus == STATUS_READY:
            # Start the download
            self.downloadWaveformData()

    @QtCore.pyqtSlot()
    def onDownloadCancel(self):
        if self.waveformsDownloadStatus == STATUS_WORKING:
            # Cancel running download
            self.waveforms_handler.cancel_download()

    def updateToolbars(self):
        """
        Update the UI elements to reflect the current status
        """
        self.downloadPushButton.setEnabled(
            self.waveformsDownloadStatus == STATUS_READY)

    @QtCore.pyqtSlot()
    def onSavePushButton(self):
        """
        Triggered after savePushButton is toggled.
        """
        if self.waveformsSaveStatus != STATUS_WORKING:
            self.waveformsSaveStatus = STATUS_WORKING
            self.saveSpinner.show()
            if self.waveformsDownloadStatus == STATUS_DONE:
                # If any downloads are complete, we can trigger the save now
                self.saveWaveformData()
            else:
                # Otherwise we have to wait until the download is done, indicate this to the user
                self.saveSpinner.setLabel("Waiting for downloads to finish")
                # If not already downloading, try to start
                if self.waveformsDownloadStatus != STATUS_WORKING:
                    self.downloadWaveformData()

    @QtCore.pyqtSlot()
    def resetDownload(self):
        """
        This function is triggered whenever the values in secondsBeforeSpinBox or
        secondsAfterSpinBox are changed. Any change means that we need to wipe out
        all the downloads that have occurred and start over.
        """
        LOGGER.debug("Download button reset")
        self.waveformsDownloadStatus = STATUS_READY
        self.resetSave()

    @QtCore.pyqtSlot()
    def resetSave(self):
        """
        This function is triggered whenever the values in saveDirectory or
        saveFormat elements are changed. Any change means that we need to
        start saving from the beginning.
        """
        LOGGER.debug("Save button reset")
        self.waveformsSaveStatus = STATUS_READY
        self.updateToolbars()

    def downloadWaveformData(self):
        """
        This function is triggered after the selectionTable is initially loaded
        by loadWaveformChoices() and, after that, by handleWaveformResponse() after
        it has finished handling a waveform.

        This function looks at the current selectionTable view for any waveforms
        that have not yet been downloaded. After that table is exhausted, it goes
        through all not-yet-downloaded data in waveformHandler.currentDF.
        """

        LOGGER.info("Starting download of waveform data")

        self.waveformsDownloadStatus = STATUS_WORKING
        self.downloadCount = len(self.waveforms_handler.waveforms)
        self.downloadCompleted = 0
        self.downloadSpinner.show()
        self.updateToolbars()

        # Priority is given to waveforms shown on the screen
        priority_ids = [
            waveform.waveform_id
            for waveform in self.waveforms_handler.waveforms
        ]
        other_ids = []

        self.waveforms_handler.download_waveforms(
            priority_ids, other_ids, self.timeWindowAdapter.timeWindow)

        # Update the table rows
        for row in range(self.selectionTable.rowCount()):
            waveform_id = self.selectionTable.item(row,
                                                   WAVEFORM_ID_COLUMN).text()
            waveform = self.waveforms_handler.waveforms_by_id.get(waveform_id)
            self.selectionTable.item(
                row, WAVEFORM_IMAGE_COLUMN).setWaveform(waveform)

    def getTableRow(self, waveform_id):
        """
        Get the table row for a given waveform
        """
        for row in range(self.selectionTable.rowCount()):
            if self.selectionTable.item(
                    row, WAVEFORM_ID_COLUMN).text() == waveform_id:
                return row
        return None

    @QtCore.pyqtSlot(object)
    def onWaveformDownloaded(self, result):
        """
        Called each time a waveform request has completed
        """
        waveform_id = result.waveform_id
        LOGGER.debug("Ready to display waveform %s (%s)", waveform_id,
                     QtCore.QThread.currentThreadId())

        self.downloadCompleted += 1
        msg = "Downloaded %d of %d" % (self.downloadCompleted,
                                       self.downloadCount)
        # self.downloadStatusLabel.setText(msg)
        self.downloadSpinner.setLabel(msg)

        row = self.getTableRow(waveform_id)
        if row is None:
            LOGGER.error("Couldn't find a row for waveform %s", waveform_id)
            return

        waveform = self.waveforms_handler.waveforms_by_id.get(waveform_id)
        self.selectionTable.item(row,
                                 WAVEFORM_IMAGE_COLUMN).setWaveform(waveform)

        LOGGER.debug("Displayed waveform %s", waveform_id)

    @QtCore.pyqtSlot(object)
    def onAllDownloaded(self, result):
        """
        Called after all waveforms have been downloaded
        """
        LOGGER.debug('COMPLETED all downloads')

        if self.waveformsDownloadStatus == STATUS_WORKING:
            self.downloadSpinner.hide()

            # If normal result, mark as done. If an error, mark as ready (ie. user can download again)
            if isinstance(result, Exception):
                self.waveformsDownloadStatus = STATUS_READY
            else:
                self.waveformsDownloadStatus = STATUS_DONE
                self.downloadStatusLabel.setText(
                    "Downloaded %d waveforms" %
                    len(self.waveforms_handler.waveforms))

                # Initiate save if that was queued
                if self.waveformsSaveStatus == STATUS_WORKING:
                    self.saveWaveformData()

            self.updateToolbars()

    @QtCore.pyqtSlot()
    def saveWaveformData(self):
        """
        Save waveforms after all downloads are complete.
        """

        # Update status
        self.waveformsSaveStatus = STATUS_WORKING
        self.updateToolbars()

        # Update GUI in case we came from an internal call
        QtWidgets.QApplication.processEvents()

        errors = []
        savedCount = 0
        skippedCount = 0

        try:
            waveforms = self.iterWaveforms(saveable_only=True)
            outputDir = self.waveformDirectory
            outputFormat = self.saveFormatAdapter.getValue()

            for result in self.waveforms_handler.save_waveforms_iter(
                    outputDir, outputFormat, waveforms):
                if isinstance(result.result, Exception):
                    LOGGER.error("Failed to save waveform %s: %s",
                                 result.waveform_id, result.result)
                    errors.append("%s: %s" %
                                  (result.waveform_id, result.result))
                elif result.result:
                    savedCount += 1
                    self.saveSpinner.setLabel("Saved %d waveforms" %
                                              savedCount)
                    QtWidgets.QApplication.processEvents()  # update GUI
                else:
                    skippedCount += 1
            self.saveStatusLabel.setText("Saved %d waveforms" % savedCount)
            self.saveStatusLabel.repaint()

            LOGGER.info(
                "Save complete: %d saved, %d already existed, %d errors",
                savedCount, skippedCount, len(errors))

            if errors:
                # Truncate the list of errors if it's very long
                errorCount = len(errors)
                if errorCount > 20:
                    errors = errors[:20]
                    errors.append("(see log for full list)")
                raise Exception("%d waveforms couldn't be saved:\n%s" %
                                (errorCount, "\n".join(errors)))

            self.waveformsSaveStatus = STATUS_DONE
        except Exception as e:
            LOGGER.error(e)
            self.waveformsSaveStatus = STATUS_ERROR
            QtWidgets.QMessageBox.critical(self, "Error", str(e))
        finally:
            self.updateToolbars()

        self.saveSpinner.hide()
        LOGGER.debug('COMPLETED saving all waveforms')

    @QtCore.pyqtSlot()
    def getWaveformDirectory(self):
        """
        This function is triggered whenever the user presses the "to <directory>" button.
        """
        # If the user quits or cancels this dialog, '' is returned
        newDirectory = str(
            QtWidgets.QFileDialog.getExistingDirectory(
                self, "Waveform Directory", self.waveformDirectory,
                QtWidgets.QFileDialog.ShowDirsOnly))

        if newDirectory != '':
            self.waveformDirectory = newDirectory
            self.saveDirectoryPushButton.setText(self.waveformDirectory)
            self.resetSave()

    @QtCore.pyqtSlot()
    def browseWaveformDirectory(self):
        """
        This function is triggered whenever the user presses the "browse" button.
        """
        url = QtCore.QUrl.fromLocalFile(self.waveformDirectory)
        QtGui.QDesktopServices.openUrl(url)

    def loadPreferences(self):
        """
        Load preferences relevant to this widget
        """
        prefs = self.pyweed.preferences

        self.waveformDirectory = prefs.Waveforms.saveDir

        self.timeWindowAdapter.setValues(
            safe_int(prefs.Waveforms.timeWindowBefore, 60),
            safe_int(prefs.Waveforms.timeWindowAfter,
                     600), prefs.Waveforms.timeWindowBeforePhase,
            prefs.Waveforms.timeWindowAfterPhase)

        self.saveFormatAdapter.setValue(prefs.Waveforms.saveFormat)

    def savePreferences(self):
        """
        Save preferences related to the controls on this widget
        """
        prefs = self.pyweed.preferences

        prefs.Waveforms.saveDir = self.waveformDirectory
        timeWindow = self.timeWindowAdapter.timeWindow
        prefs.Waveforms.timeWindowBefore = timeWindow.start_offset
        prefs.Waveforms.timeWindowAfter = timeWindow.end_offset
        prefs.Waveforms.timeWindowBeforePhase = timeWindow.start_phase
        prefs.Waveforms.timeWindowAfterPhase = timeWindow.end_phase

        prefs.Waveforms.saveFormat = self.saveFormatAdapter.getValue()