diff --git a/QtPyLoT.py b/QtPyLoT.py index 8e558b5c..d8ea085b 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -106,7 +106,9 @@ class MainWindow(QMainWindow): self._inputs = AutoPickParameter(infile) self._props = None + self.dirty = False self.project = Project() + self.project.parameter = self._inputs self.tap = None self.paraBox = None self.array_map = None @@ -143,8 +145,6 @@ class MainWindow(QMainWindow): self.data = Data(self) self.autodata = Data(self) - self.dirty = False - if settings.value("user/FullName", None) is None: fulluser = QInputDialog.getText(self, "Enter Name:", "Full name") settings.setValue("user/FullName", fulluser) @@ -172,7 +172,6 @@ class MainWindow(QMainWindow): self.setupUi() self.filteroptions = {} - self.pickDlgs = {} self.picks = {} self.autopicks = {} self.loc = False @@ -362,10 +361,10 @@ class MainWindow(QMainWindow): QCoreApplication.instance().quit, QKeySequence.Close, quitIcon, "Close event and quit PyLoT") - self.parameterAction = self.createAction(self, "Pick Parameter", - self.pickParameter, + self.parameterAction = self.createAction(self, "Parameter", + self.setParameter, None, QIcon(None), - "Modify Picking Parameter") + "Modify Parameter") self.filterAction = self.createAction(self, "&Filter ...", self.filterWaveformData, "Ctrl+F", filter_icon, @@ -756,7 +755,7 @@ class MainWindow(QMainWindow): Creates and adds events by user selection of event folders to GUI. ''' if not self.project: - self.project = Project() + self.createNewProject() ed = getExistingDirectories(self, 'Select event directories...') if ed.exec_(): eventlist = ed.selectedFiles() @@ -764,11 +763,55 @@ class MainWindow(QMainWindow): eventlist = [item for item in eventlist if item.split('/')[-1].startswith('e') and len(item.split('/')[-1].split('.')) == 3 and len(item.split('/')[-1]) == 12] + if not eventlist: + print('No events found! Expected structure for event folders: [evID.DOY.YR]') + return else: return if not self.project: print('No project found.') return + + #get path from first event in list and split them + path = eventlist[0] + try: + dirs = { + 'database': path.split('/')[-2], + 'datapath': path.split('/')[-3], + 'rootpath': os.path.join(*path.split('/')[:-3]) + } + except Exception as e: + dirs = { + 'database': '', + 'datapath': '', + 'rootpath': '' + } + print('Warning: Could not automatically init folder structure. ({})'.format(e)) + + if not self.project.eventlist: + #init parameter object + self.setParameter(show=False) + #hide all parameter (show all needed parameter later) + self.paraBox.hide_parameter() + for directory in dirs.keys(): + #set parameter + box = self.paraBox.boxes[directory] + self.paraBox.setValue(box, dirs[directory]) + #show needed parameter in box + self.paraBox.show_parameter(directory) + dirs_box = self.paraBox.get_groupbox_exclusive('Directories') + if not dirs_box.exec_(): + return + self.project.rootpath = dirs['rootpath'] + else: + if hasattr(self.project, 'rootpath'): + if not self.project.rootpath == dirs['rootpath']: + QMessageBox.warning(self, "PyLoT Warning", + 'Rootpath missmatch to current project!') + return + else: + self.project.rootpath = dirs['rootpath'] + self.project.add_eventlist(eventlist) self.init_events() self.setDirty(True) @@ -1314,6 +1357,8 @@ class MainWindow(QMainWindow): self.disconnectWFplotEvents() if self.pg: self.dataPlot.plotWidget.getPlotItem().clear() + self.dataPlot.plotWidget.hideAxis('bottom') + self.dataPlot.plotWidget.hideAxis('left') else: self.dataPlot.getAxes().cla() self.loadlocationaction.setEnabled(False) @@ -1519,7 +1564,9 @@ class MainWindow(QMainWindow): wfID = self.getWFID(ycoord) if wfID is None: return - + self.pickDialog(wfID) + + def pickDialog(self, wfID, nextStation=False): station = self.getStationName(wfID) if not station: return @@ -1530,21 +1577,23 @@ class MainWindow(QMainWindow): station=station, picks=self.getPicksOnStation(station, 'manual'), autopicks=self.getPicksOnStation(station, 'auto')) + pickDlg.nextStation.setChecked(nextStation) if pickDlg.exec_(): - if not pickDlg.getPicks(): - return - self.setDirty(True) - self.update_status('picks accepted ({0})'.format(station)) - replot = self.addPicks(station, pickDlg.getPicks()) - self.get_current_event().setPick(station, pickDlg.getPicks()) - self.enableSaveManualPicksAction() - if replot: - self.plotWaveformDataThread() - self.drawPicks() - self.draw() - else: - self.drawPicks(station) - self.draw() + if pickDlg.getPicks(): + self.setDirty(True) + self.update_status('picks accepted ({0})'.format(station)) + replot = self.addPicks(station, pickDlg.getPicks()) + self.get_current_event().setPick(station, pickDlg.getPicks()) + self.enableSaveManualPicksAction() + if replot: + self.plotWaveformDataThread() + self.drawPicks() + self.draw() + else: + self.drawPicks(station) + self.draw() + if pickDlg.nextStation.isChecked(): + self.pickDialog(wfID - 1, nextStation=pickDlg.nextStation.isChecked()) else: self.update_status('picks discarded ({0})'.format(station)) if not self.get_loc_flag() and self.check4Loc(): @@ -2175,28 +2224,18 @@ class MainWindow(QMainWindow): self.data = Data(self, evtdata=event) self.setDirty(True) - def createNewProject(self, exists=False): + def createNewProject(self): ''' Create new project file. ''' - if not exists: - if not self.okToContinue(): - return - dlg = QFileDialog() - fnm = dlg.getSaveFileName(self, 'Create a new project file...', filter='Pylot project (*.plp)') - filename = fnm[0] - if not len(fnm[0]): - return False - if not filename.split('.')[-1] == 'plp': - filename = fnm[0] + '.plp' - if not exists: - self.project = Project() - self.init_events(new=True) - self.setDirty(True) - self.project.parameter=self._inputs - self.project.save(filename) + if not self.okToContinue(): + return + self.project = Project() + self.init_events(new=True) self.setDirty(False) - self.update_status('Creating new project...', duration=1000) + self.project.parameter=self._inputs + self.saveProjectAsAction.setEnabled(True) + self.update_status('Created new project...', duration=1000) return True def loadProject(self, fnm=None): @@ -2225,8 +2264,26 @@ class MainWindow(QMainWindow): return self.init_array_tab() - def saveProjectAs(self): - self.saveProject(new=True) + def saveProjectAs(self, exists=False): + ''' + Save back project to new pickle file. + ''' + if not exists: + if not self.okToContinue(): + return + dlg = QFileDialog() + fnm = dlg.getSaveFileName(self, 'Create a new project file...', filter='Pylot project (*.plp)') + filename = fnm[0] + if not len(fnm[0]): + return False + if not filename.split('.')[-1] == 'plp': + filename = fnm[0] + '.plp' + self.project.parameter=self._inputs + self.project.save(filename) + self.setDirty(False) + self.saveProjectAsAction.setEnabled(True) + self.update_status('Saved new project to {}'.format(filename), duration=5000) + return True def saveProject(self, new=False): ''' @@ -2234,14 +2291,14 @@ class MainWindow(QMainWindow): ''' if self.project and not new: if not self.project.location: - if not self.createNewProject(exists=True): + if not self.saveProjectAs(exists=True): self.setDirty(True) return False else: self.project.parameter=self._inputs self.project.save() if not self.project.dirty: - print('Saved back project to file:\n{}'.format(self.project.location)) + self.update_status('Saved back project to file:\n{}'.format(self.project.location), duration=5000) self.setDirty(False) return True else: @@ -2249,7 +2306,7 @@ class MainWindow(QMainWindow): qmb = QMessageBox.warning(self,'Could not save project', 'Could not save back to original file.\nChoose new file') self.setDirty(True) - return self.createNewProject(exists=True) + return self.saveProjectAs(exists=True) def draw(self): self.fill_eventbox() @@ -2260,7 +2317,7 @@ class MainWindow(QMainWindow): def setDirty(self, value): self.saveProjectAction.setEnabled(value) - self.saveProjectAsAction.setEnabled(value) + self.saveProjectAsAction.setEnabled(True) self.project.setDirty(value) self.dirty = value @@ -2272,12 +2329,13 @@ class MainWindow(QMainWindow): # self.closing.emit() # QMainWindow.closeEvent(self, event) - def pickParameter(self): + def setParameter(self, show=True): if not self.paraBox: self.paraBox = AutoPickParaBox(self._inputs) self.paraBox._apply.clicked.connect(self._setDirty) - self.paraBox._okay.clicked.connect(self._setDirty) - self.paraBox.show() + self.paraBox._okay.clicked.connect(self._setDirty) + if show: + self.paraBox.show() def PyLoTprefs(self): if not self._props: @@ -2303,6 +2361,7 @@ class Project(object): def __init__(self): self.eventlist = [] self.location = None + self.rootpath = None self.dirty = False self.parameter = None self._table = None @@ -2316,6 +2375,9 @@ class Project(object): return for item in eventlist: event = Event(item) + event.rootpath = self.parameter['rootpath'] + event.database = self.parameter['database'] + event.datapath = self.parameter['datapath'] if not event.path in self.getPaths(): self.eventlist.append(event) self.setDirty() @@ -2387,6 +2449,9 @@ class Event(object): ''' def __init__(self, path): self.path = path + self.database = path.split('/')[-2] + self.datapath = path.split('/')[-3] + self.rootpath = os.path.join(*path.split('/')[:-3]) self.autopicks = {} self.picks = {} self.notes = '' diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index e1085601..a281ef17 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -ab97-dirty +6feff-dirty diff --git a/pylot/core/io/default_parameters.py b/pylot/core/io/default_parameters.py index 1ae7caad..563476b8 100644 --- a/pylot/core/io/default_parameters.py +++ b/pylot/core/io/default_parameters.py @@ -277,8 +277,12 @@ defaults = {'rootpath': {'type': str, 'tooltip': 'maximum allowed deviation from Wadati-diagram', 'value': 1.0}, - 'localMag': {'type': float, - 'tooltip': 'maximum allowed deviation from Wadati-diagram', + 'WAscaling': {'type': float, + 'tooltip': 'Scaling relation of Wood-Anderson amplitude [nm]', + 'value': 1.0}, + + 'magscaling': {'type': float, + 'tooltip': 'Scaling relation for derived local magnitude [a*Ml+b]', 'value': 1.0} } @@ -301,8 +305,10 @@ settings_main={ 'smoment':[ 'vp', 'rho', - 'Qp', - 'localMag'], + 'Qp'], + 'localmag':[ + 'WAscaling', + 'magscaling'], 'pick':[ 'extent', 'pstart', diff --git a/pylot/core/io/inputs.py b/pylot/core/io/inputs.py index f70b8caf..cd62b96a 100644 --- a/pylot/core/io/inputs.py +++ b/pylot/core/io/inputs.py @@ -140,7 +140,8 @@ class AutoPickParameter(object): all_names += self.get_main_para_names()['dirs'] all_names += self.get_main_para_names()['nlloc'] all_names += self.get_main_para_names()['smoment'] - all_names += self.get_main_para_names()['pick'] + all_names += self.get_main_para_names()['localmag'] + all_names += self.get_main_para_names()['pick'] all_names += self.get_special_para_names()['z'] all_names += self.get_special_para_names()['h'] all_names += self.get_special_para_names()['fm'] @@ -234,6 +235,8 @@ class AutoPickParameter(object): 'NLLoc settings', seperator) self.write_section(fid_out, self.get_main_para_names()['smoment'], 'parameters for seismic moment estimation', seperator) + self.write_section(fid_out, self.get_main_para_names()['localmag'], + 'settings local magnitude', seperator) self.write_section(fid_out, self.get_main_para_names()['pick'], 'common settings picker', seperator) fid_out.write(('#special settings for calculating CF#\n'+ diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index a92a7940..9a051139 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -752,6 +752,7 @@ class PickDlg(QDialog): self._init_autopicks = {} self.filteroptions = FILTERDEFAULTS self.pick_block = False + self.nextStation = QtGui.QCheckBox('Continue with next station.') # initialize panning attributes self.press = None @@ -845,17 +846,21 @@ class PickDlg(QDialog): self.s_button = QPushButton('S', self) self.p_button.setCheckable(True) self.s_button.setCheckable(True) - # button shortcuts (1 for P-button, 2 for S-button) - self.p_button.setShortcut(QKeySequence('1')) - self.s_button.setShortcut(QKeySequence('2')) # set button tooltips self.p_button.setToolTip('Hotkey: "1"') self.s_button.setToolTip('Hotkey: "2"') - + # create accept/reject button self.accept_button = QPushButton('&Accept Picks') self.reject_button = QPushButton('&Reject Picks') self.disable_ar_buttons() + + # add hotkeys + self._shortcut_space = QtGui.QShortcut(QtGui.QKeySequence(' '), self) + self._shortcut_space.activated.connect(self.accept_button.clicked) + # button shortcuts (1 for P-button, 2 for S-button) + self.p_button.setShortcut(QKeySequence('1')) + self.s_button.setShortcut(QKeySequence('2')) # layout the outermost appearance of the Pick Dialog _outerlayout = QVBoxLayout() @@ -872,7 +877,9 @@ class PickDlg(QDialog): _dialtoolbar.addAction(self.resetPicksAction) if self._embedded: _dialtoolbar.addWidget(self.accept_button) - _dialtoolbar.addWidget(self.reject_button) + _dialtoolbar.addWidget(self.reject_button) + else: + _dialtoolbar.addWidget(self.nextStation) # layout the innermost widget _innerlayout = QVBoxLayout() @@ -1724,7 +1731,6 @@ class TuneAutopicker(QWidget): pickDlg.update_picks.connect(self.picks_from_pickdlg) pickDlg.update_picks.connect(self.fill_eventbox) pickDlg.update_picks.connect(self.fill_stationbox) - pickDlg.update_picks.connect(self.parent.drawPicks) pickDlg.update_picks.connect(lambda: self.parent.setDirty(True)) pickDlg.update_picks.connect(self.parent.enableSaveManualPicksAction) self.pickDlg = QtGui.QWidget() @@ -1734,7 +1740,15 @@ class TuneAutopicker(QWidget): def picks_from_pickdlg(self, picks=None): station = self.get_current_station() + replot = self.parent.addPicks(station, picks) self.get_current_event().setPick(station, picks) + if self.get_current_event() == self.parent.get_current_event(): + if replot: + self.parent.plotWaveformDataThread() + self.parent.drawPicks() + else: + self.parent.drawPicks(station) + self.parent.draw() def plot_manual_picks_to_figs(self): picks = self.get_current_event_picks(self.get_current_station()) @@ -1843,7 +1857,7 @@ class TuneAutopicker(QWidget): def fill_eventbox(self): # update own list self.parent.fill_eventbox(eventBox=self.eventBox, select_events='ref') - index_start = self.eventBox.currentIndex() + index_start = self.parent.eventBox.currentIndex() index = index_start if index == -1: index += 1 @@ -1979,7 +1993,8 @@ class AutoPickParaBox(QtGui.QWidget): self.add_special_pick_parameters_tab() self.params_to_gui() self._toggle_advanced_settings() - self.resize(720, 1280) + self.resize(720, 1280) + self.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal) def _init_sublayouts(self): self._main_layout = QtGui.QVBoxLayout() @@ -2107,31 +2122,31 @@ class AutoPickParaBox(QtGui.QWidget): scrollA = QtGui.QScrollArea() scrollA.setWidgetResizable(True) scrollA.setWidget(widget) - widget.setLayout(layout) - self.tabs.addTab(scrollA, name) def add_main_parameters_tab(self): self.add_to_layout(self._main_layout, 'Directories', - self.parameter.get_main_para_names()['dirs']) + self.parameter.get_main_para_names()['dirs'], 0) self.add_to_layout(self._main_layout, 'NLLoc', - self.parameter.get_main_para_names()['nlloc']) + self.parameter.get_main_para_names()['nlloc'], 1) self.add_to_layout(self._main_layout, 'Seismic Moment', - self.parameter.get_main_para_names()['smoment']) + self.parameter.get_main_para_names()['smoment'], 2) + self.add_to_layout(self._main_layout, 'Local Magnitude', + self.parameter.get_main_para_names()['localmag'], 3) self.add_to_layout(self._main_layout, 'Common Settings Characteristic Function', - self.parameter.get_main_para_names()['pick']) + self.parameter.get_main_para_names()['pick'], 4) self.add_tab(self._main_layout, 'Main Settings') def add_special_pick_parameters_tab(self): self.add_to_layout(self._advanced_layout, 'Z-component', - self.parameter.get_special_para_names()['z']) + self.parameter.get_special_para_names()['z'], 0) self.add_to_layout(self._advanced_layout, 'H-components', - self.parameter.get_special_para_names()['h']) + self.parameter.get_special_para_names()['h'], 1) self.add_to_layout(self._advanced_layout, 'First-motion picker', - self.parameter.get_special_para_names()['fm']) + self.parameter.get_special_para_names()['fm'], 2) self.add_to_layout(self._advanced_layout, 'Quality assessment', - self.parameter.get_special_para_names()['quality']) + self.parameter.get_special_para_names()['quality'], 3) self.add_tab(self._advanced_layout, 'Advanced Settings') # def gen_h_seperator(self): @@ -2145,12 +2160,37 @@ class AutoPickParaBox(QtGui.QWidget): # font.setBold(True) # label.setFont(font) # return label + + def refresh(self): + for groupbox in self.groupboxes.values(): + layout = groupbox._parentLayout + position = groupbox._position + layout.insertWidget(position, groupbox) + + def get_groupbox_exclusive(self, name): + dialog = QtGui.QDialog(self.parent()) + buttonbox = QtGui.QDialogButtonBox(QDialogButtonBox.Ok | + QDialogButtonBox.Cancel) + self._exclusive_dialog = dialog + layout = QtGui.QVBoxLayout() + dialog.setLayout(layout) + layout.addWidget(self.groupboxes[name]) + layout.addWidget(buttonbox) + buttonbox.accepted.connect(dialog.accept) + buttonbox.accepted.connect(self.refresh) + buttonbox.accepted.connect(self.params_from_gui) + buttonbox.rejected.connect(dialog.reject) + buttonbox.rejected.connect(self.refresh) + buttonbox.rejected.connect(self.params_to_gui) + return dialog - def add_to_layout(self, layout, name, items): + def add_to_layout(self, layout, name, items, position): groupbox = QtGui.QGroupBox(name) + groupbox._position = position + groupbox._parentLayout = layout self.groupboxes[name] = groupbox groupbox.setLayout(self.init_boxes(items)) - layout.addWidget(groupbox) + layout.insertWidget(position, groupbox) def show_groupboxes(self): for name in self.groupboxes.keys(): @@ -2174,6 +2214,16 @@ class AutoPickParaBox(QtGui.QWidget): else: print('Groupbox {} not part of object.'.format(name)) + def show_file_buttons(self): + self.saveButton.show() + self.loadButton.show() + self.defaultsButton.show() + + def hide_file_buttons(self): + self.saveButton.hide() + self.loadButton.hide() + self.defaultsButton.hide() + def show_parameter(self, name=None): if not name: for name in self.boxes.keys(): @@ -2286,6 +2336,13 @@ class AutoPickParaBox(QtGui.QWidget): except Exception as e: self._warn('Could not restore defaults:\n{}'.format(e)) return + + def show(self): + self.refresh() + self.show_parameter() + if hasattr(self, '_exclusive_dialog'): + self._exclusive_dialog.close() + QtGui.QWidget.show(self) def _warn(self, message): self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Warning,