diff --git a/QtPyLoT.py b/QtPyLoT.py
index f75b322b..1e4b2147 100755
--- a/QtPyLoT.py
+++ b/QtPyLoT.py
@@ -30,12 +30,14 @@ import matplotlib
matplotlib.use('Qt4Agg')
matplotlib.rcParams['backend.qt4'] = 'PySide'
+from PySide import QtGui, QtCore
from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, \
QFileInfo, Qt, QSize
from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \
- QWidget, QHBoxLayout, QStyle, QKeySequence, QLabel, QFrame, QAction, \
+ QWidget, QHBoxLayout, QVBoxLayout, QStyle, QKeySequence, QLabel, QFrame, QAction, \
QDialog, QErrorMessage, QApplication, QPixmap, QMessageBox, QSplashScreen, \
- QActionGroup, QListWidget, QDockWidget, QLineEdit
+ QActionGroup, QListWidget, QDockWidget, QLineEdit, QListView, QAbstractItemView, \
+ QTreeView, QComboBox, QTabWidget, QPushButton, QGridLayout
import numpy as np
from obspy import UTCDateTime
@@ -60,7 +62,7 @@ from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \
getDataType, ComparisonDialog
from pylot.core.util.map_projection import map_projection
from pylot.core.util.structure import DATASTRUCTURE
-from pylot.core.util.thread import AutoPickThread
+from pylot.core.util.thread import AutoPickThread, Thread
from pylot.core.util.version import get_git_version as _getVersionString
import icons_rc
@@ -86,12 +88,32 @@ class MainWindow(QMainWindow):
else:
self.infile = infile
+ self.project = Project()
+ self.array_map = None
+ self._metadata = None
+ self._eventChanged = [False, False]
+
+ self.poS_id = None
+ self.ae_id = None
+
# UI has to be set up before(!) children widgets are about to show up
self.createAction = createAction
# read settings
settings = QSettings()
self.recentfiles = settings.value("data/recentEvents", [])
self.dispComponent = str(settings.value("plotting/dispComponent", "Z"))
+
+ # initialize event data
+ if self.recentfiles:
+ lastEvent = self.getLastEvent()
+ self.data = Data(self, lastEvent)
+ else:
+ self.data = Data(self)
+ self.autodata = Data(self)
+
+ self.dirty = False
+
+ # setup UI
self.setupUi()
if settings.value("user/FullName", None) is None:
@@ -120,22 +142,8 @@ class MainWindow(QMainWindow):
self.picks = {}
self.autopicks = {}
self.loc = False
- self._metadata = None
-
- self.array_map = None
-
- # initialize event data
- if self.recentfiles:
- lastEvent = self.getLastEvent()
- self.data = Data(self, lastEvent)
- else:
- self.data = Data(self)
- self.autodata = Data(self)
-
- self.dirty = False
def setupUi(self):
-
try:
self.startTime = min(
[tr.stats.starttime for tr in self.data.wfdata])
@@ -151,25 +159,54 @@ class MainWindow(QMainWindow):
xlab = self.startTime.strftime('seconds since %Y/%m/%d %H:%M:%S (%Z)')
_widget = QWidget()
- _widget.setCursor(Qt.CrossCursor)
- _layout = QHBoxLayout()
+ self._main_layout = QVBoxLayout()
- plottitle = "Overview: {0} components ".format(self.getComponent())
+ # add event combo box
+ self.eventBox = self.createEventBox()
+ self.eventBox.setMaxVisibleItems(30)
+ self.eventBox.setEnabled(False)
+ self._event_layout = QHBoxLayout()
+ self._event_layout.addWidget(QLabel('Event: '))
+ self._event_layout.addWidget(self.eventBox)
+ self._event_layout.setStretch(1,1) #set stretch of item 1 to 1
+ self._main_layout.addLayout(self._event_layout)
+ self.eventBox.activated.connect(self.refreshEvents)
+
+ # add tabs
+ self.tabs = QTabWidget()
+ self._main_layout.addWidget(self.tabs)
+ self.tabs.currentChanged.connect(self.refreshTabs)
# create central matplotlib figure canvas widget
- self.DataPlot = WaveformWidget(parent=self, xlabel=xlab, ylabel=None,
+ plottitle = "Overview: {0} components ".format(self.getComponent())
+ self.dataPlot = WaveformWidget(parent=self, xlabel=xlab, ylabel=None,
title=plottitle)
- self.DataPlot.mpl_connect('button_press_event',
- self.pickOnStation)
- self.DataPlot.mpl_connect('axes_enter_event',
- lambda event: self.tutor_user())
- _layout.addWidget(self.DataPlot)
-
+ self.dataPlot.setCursor(Qt.CrossCursor)
+ wf_tab = QtGui.QWidget()
+ array_tab = QtGui.QWidget()
+ events_tab = QtGui.QWidget()
+ self.wf_layout = QtGui.QVBoxLayout()
+ self.array_layout = QtGui.QVBoxLayout()
+ self.events_layout = QtGui.QVBoxLayout()
+ wf_tab.setLayout(self.wf_layout)
+ array_tab.setLayout(self.array_layout)
+ events_tab.setLayout(self.events_layout)
+ self.tabs.addTab(wf_tab, 'Waveform Plot')
+ self.tabs.addTab(array_tab, 'Array Map')
+ self.tabs.addTab(events_tab, 'Eventlist')
+
+ self.wf_layout.addWidget(self.dataPlot)
+ self.init_array_tab()
+ self.init_event_table()
+ self.tabs.setCurrentIndex(0)
+
quitIcon = self.style().standardIcon(QStyle.SP_MediaStop)
saveIcon = self.style().standardIcon(QStyle.SP_DriveHDIcon)
+ openIcon = self.style().standardIcon(QStyle.SP_DirOpenIcon)
helpIcon = self.style().standardIcon(QStyle.SP_DialogHelpButton)
newIcon = self.style().standardIcon(QStyle.SP_FileIcon)
-
+ newFolderIcon = self.style().standardIcon(QStyle.SP_FileDialogNewFolder)
+
# create resource icons
locactionicon = QIcon()
locactionicon.addPixmap(QPixmap(':/icons/locactionicon.png'))
@@ -177,6 +214,14 @@ class MainWindow(QMainWindow):
manupicksicon.addPixmap(QPixmap(':/icons/manupicsicon.png'))
autopicksicon = QIcon()
autopicksicon.addPixmap(QPixmap(':/icons/autopicsicon.png'))
+ self.autopicksicon_small = QIcon()
+ self.autopicksicon_small.addPixmap(QPixmap(':/icons/autopicsicon.png'))
+ self.manupicksicon_small = QIcon()
+ self.manupicksicon_small.addPixmap(QPixmap(':/icons/manupicsicon.png'))
+ # self.autopicksicon_small = QIcon()
+ # self.autopicksicon_small.addPixmap(QPixmap(':/icons/autopicksicon_small.png'))
+ # self.manupicksicon_small = QIcon()
+ # self.manupicksicon_small.addPixmap(QPixmap(':/icons/manupicksicon_small.png'))
loadpiloticon = QIcon()
loadpiloticon.addPixmap(QPixmap(':/icons/Matlab_PILOT_icon.png'))
p_icon = QIcon()
@@ -199,10 +244,24 @@ class MainWindow(QMainWindow):
locate_icon.addPixmap(QPixmap(':/icons/locate_button.png'))
compare_icon = QIcon()
compare_icon.addPixmap(QPixmap(':/icons/compare_button.png'))
- newEventAction = self.createAction(self, "&New event ...",
- self.createNewEvent,
+ self.newProjectAction = self.createAction(self, "&New project ...",
+ self.createNewProject,
QKeySequence.New, newIcon,
- "Create a new event.")
+ "Create a new Project.")
+ self.openProjectAction = self.createAction(self, "Load project ...",
+ self.loadProject,
+ QKeySequence.Open,
+ openIcon,
+ "Load project file")
+ self.saveProjectAction = self.createAction(self, "Save project ...",
+ self.saveProject,
+ QKeySequence.Save,
+ saveIcon,
+ "Save project file")
+ # newEventAction = self.createAction(self, "&New event ...",
+ # self.createNewEvent,
+ # QKeySequence.New, newIcon,
+ # "Create a new event.")
self.openmanualpicksaction = self.createAction(self, "Load &picks ...",
self.load_data,
QKeySequence.Open,
@@ -228,11 +287,11 @@ class MainWindow(QMainWindow):
"Load location information on "
"the displayed event.")
self.loadpilotevent = self.createAction(self, "Load PILOT &event ...",
- self.load_pilotevent, "Ctrl+E",
- loadpiloticon,
- "Load PILOT event from information "
- "MatLab binary collections (created"
- " in former MatLab based version).")
+ self.load_pilotevent, "Ctrl+E",
+ loadpiloticon,
+ "Load PILOT event from information "
+ "MatLab binary collections (created"
+ " in former MatLab based version).")
self.loadpilotevent.setEnabled(False)
self.saveEventAction = self.createAction(self, "&Save event ...",
@@ -240,11 +299,10 @@ class MainWindow(QMainWindow):
saveIcon, "Save actual event data.")
self.saveEventAction.setEnabled(False)
- openWFDataAction = self.createAction(self, "Open &waveforms ...",
- self.loadWaveformData,
- "Ctrl+W", QIcon(":/wfIcon.png"),
- "Open waveform data (event will "
- "be closed)")
+ self.addEventDataAction = self.createAction(self, "Add &events ...",
+ self.add_events,
+ "Ctrl+W", newFolderIcon,
+ "Add event data")
prefsEventAction = self.createAction(self, "Preferences",
self.PyLoTprefs,
QKeySequence.Preferences,
@@ -290,8 +348,9 @@ class MainWindow(QMainWindow):
homepage (internet connection available),
or shipped documentation files.""")
self.fileMenu = self.menuBar().addMenu('&File')
- self.fileMenuActions = (newEventAction, self.openmanualpicksaction,
- self.saveEventAction, openWFDataAction, None,
+ self.fileMenuActions = (self.newProjectAction, self.addEventDataAction,
+ self.openProjectAction, self.saveProjectAction,
+ self.openmanualpicksaction, self.saveEventAction, None,
prefsEventAction, quitAction)
self.fileMenu.aboutToShow.connect(self.updateFileMenu)
self.updateFileMenu()
@@ -307,7 +366,9 @@ class MainWindow(QMainWindow):
self.addActions(self.helpMenu, helpActions)
fileToolBar = self.addToolBar("FileTools")
- fileToolActions = (newEventAction, self.openmanualpicksaction,
+ fileToolActions = (self.newProjectAction, self.addEventDataAction,
+ self.openProjectAction, self.saveProjectAction,
+ self.openmanualpicksaction,
self.openautopicksaction, loadlocationaction,
self.loadpilotevent, self.saveEventAction)
fileToolBar.setObjectName("FileTools")
@@ -384,7 +445,7 @@ class MainWindow(QMainWindow):
status.addPermanentWidget(self.eventLabel)
status.showMessage("Ready", 500)
- _widget.setLayout(_layout)
+ _widget.setLayout(self._main_layout)
_widget.showFullScreen()
self.setCentralWidget(_widget)
@@ -483,6 +544,14 @@ class MainWindow(QMainWindow):
self.drawPicks(picktype=type)
self.draw()
+ def getCurrentEvent(self):
+ for event in self.project.eventlist:
+ if event.path == self.getCurrentEventPath():
+ return event
+
+ def getCurrentEventPath(self):
+ return str(self.eventBox.currentText().split('|')[0]).strip()
+
def getLastEvent(self):
return self.recentfiles[0]
@@ -523,6 +592,127 @@ class MainWindow(QMainWindow):
else:
return
+ def getWFFnames_from_eventlist(self):
+ if self.dataStructure:
+ searchPath = self.dataStructure.expandDataPath()
+ directory = self.getCurrentEventPath()
+ self.fnames = [os.path.join(directory, f) for f in os.listdir(directory)]
+ else:
+ raise DatastructureError('not specified')
+ if not self.fnames:
+ return None
+ return self.fnames
+
+ def add_events(self):
+ if not self.project:
+ self.project = Project()
+ ed = getExistingDirectories(self, 'Select event directories...')
+ if ed.exec_():
+ eventlist = ed.selectedFiles()
+ # select only folders that start with 'e', containin two dots and have length 12
+ 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]
+ else:
+ return
+ if not self.project:
+ print('No project found.')
+ return
+ self.project.add_eventlist(eventlist)
+ self.init_events()
+
+ def createEventBox(self):
+ qcb = QComboBox()
+ palette = qcb.palette()
+ palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Highlight,
+ QtGui.QBrush(QtGui.QColor(0,0,127,127)))
+ palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Highlight,
+ QtGui.QBrush(QtGui.QColor(0,0,127,127)))
+ qcb.setPalette(palette)
+ return qcb
+
+
+ def init_events(self, new=False):
+ nitems = self.eventBox.count()
+ if len(self.project.eventlist) == 0:
+ print('No events to init.')
+ self.clearWaveformDataPlot()
+ return
+ self.eventBox.setEnabled(True)
+ self.fill_eventbox()
+ if new:
+ self.eventBox.setCurrentIndex(0)
+ else:
+ self.eventBox.setCurrentIndex(nitems)
+ self.refreshEvents()
+ tabindex = self.tabs.currentIndex()
+
+ def fill_eventbox(self):
+ index=self.eventBox.currentIndex()
+ tv=QtGui.QTableView()
+ header = tv.horizontalHeader()
+ header.setResizeMode(QtGui.QHeaderView.ResizeToContents)
+ header.setStretchLastSection(True)
+ header.hide()
+ tv.verticalHeader().hide()
+ tv.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
+
+ self.eventBox.setView(tv)
+ self.eventBox.clear()
+ model = self.eventBox.model()
+ plmax=0
+ for event in self.project.eventlist:
+ pl = len(event.path)
+ if pl > plmax:
+ plmax=pl
+
+ for event in self.project.eventlist:
+ event_path = event.path
+ event_npicks = 0
+ event_nautopicks = 0
+ if event.picks:
+ event_npicks = len(event.picks)
+ if event.autopicks:
+ event_nautopicks = len(event.autopicks)
+ event_ref = event.isRefEvent()
+ event_test = event.isTestEvent()
+
+ text = '{path:{plen}} | manual: [{p:3d}] | auto: [{a:3d}]'
+ text = text.format(path=event_path,
+ plen=plmax,
+ p=event_npicks,
+ a=event_nautopicks)
+
+ item_path = QtGui.QStandardItem('{path:{plen}}'.format(path=event_path, plen=plmax))
+ item_nmp = QtGui.QStandardItem(str(event_npicks))
+ item_nmp.setIcon(self.manupicksicon_small)
+ item_nap = QtGui.QStandardItem(str(event_nautopicks))
+ item_nap.setIcon(self.autopicksicon_small)
+ item_ref = QtGui.QStandardItem()#str(event_ref))
+ item_test = QtGui.QStandardItem()#str(event_test))
+ if event_ref:
+ item_ref.setBackground(QtGui.QColor(200, 210, 230, 255))
+ if event_test:
+ item_test.setBackground(QtGui.QColor(200, 230, 200, 255))
+ item_notes = QtGui.QStandardItem(event.notes)
+
+ openIcon = self.style().standardIcon(QStyle.SP_DirOpenIcon)
+ item_path.setIcon(openIcon)
+ # if ref: set different color e.g.
+ # if event_ref:
+ # item.setBackground(QtGui.QColor(200, 210, 230, 255))
+ # if event_test:
+ # item.setBackground(QtGui.QColor(200, 230, 200, 255))
+ # item.setForeground(QtGui.QColor('black'))
+ # font = item.font()
+ # font.setPointSize(10)
+ # item.setFont(font)
+ # item2.setForeground(QtGui.QColor('black'))
+ # item2.setFont(font)
+ itemlist = [item_path, item_nmp, item_nap, item_ref, item_test, item_notes]
+ model.appendRow(itemlist)
+ self.eventBox.setCurrentIndex(index)
+
def filename_from_action(self, action):
if action.data() is None:
filt = "Supported file formats" \
@@ -648,7 +838,7 @@ class MainWindow(QMainWindow):
compare_dlg.exec_()
def getPlotWidget(self):
- return self.DataPlot
+ return self.dataPlot
@staticmethod
def getWFID(gui_event):
@@ -687,15 +877,70 @@ class MainWindow(QMainWindow):
return self.saveData()
return True
+ def refreshEvents(self):
+ self._eventChanged = [True, True]
+ self.refreshTabs()
+
+ def refreshTabs(self):
+ if self._eventChanged[0] or self._eventChanged[1]:
+ event = self.getCurrentEvent()
+ if not event.picks:
+ self.picks = {}
+ else:
+ self.picks = event.picks
+ if not event.autopicks:
+ self.autopicks = {}
+ else:
+ self.autopicks = event.autopicks
+ if self.tabs.currentIndex() == 0:
+ if hasattr(self.project, 'eventlist'):
+ if len(self.project.eventlist) > 0:
+ if self._eventChanged[0]:
+ self.newWFplot()
+ if self.tabs.currentIndex() == 1:
+ if self._eventChanged[1]:
+ self.refresh_array_map()
+ if self.tabs.currentIndex() == 2:
+ self.init_event_table()
+
+ def newWFplot(self):
+ self.loadWaveformDataThread()
+ self._eventChanged[0] = False
+
+ def loadWaveformDataThread(self):
+ wfd_thread = Thread(self, self.loadWaveformData, progressText='Reading data input...')
+ wfd_thread.finished.connect(self.plotWaveformDataThread)
+ wfd_thread.start()
+
def loadWaveformData(self):
- if self.fnames and self.okToContinue():
- self.setDirty(True)
- ans = self.data.setWFData(self.fnames)
- elif self.fnames is None and self.okToContinue():
- ans = self.data.setWFData(self.getWFFnames())
- else:
- ans = False
+ # if self.fnames and self.okToContinue():
+ # self.setDirty(True)
+ # ans = self.data.setWFData(self.fnames)
+ # elif self.fnames is None and self.okToContinue():
+ # ans = self.data.setWFData(self.getWFFnames())
+ # else:
+ # ans = False
+ self.data.setWFData(self.getWFFnames_from_eventlist())
self._stime = full_range(self.get_data().getWFData())[0]
+
+ def connectWFplotEvents(self):
+ if not self.poS_id:
+ self.poS_id = self.dataPlot.mpl_connect('button_press_event',
+ self.pickOnStation)
+ if not self.ae_id:
+ self.ae_id = self.dataPlot.mpl_connect('axes_enter_event',
+ lambda event: self.tutor_user())
+
+ def disconnectWFplotEvents(self):
+ if self.poS_id:
+ self.dataPlot.mpl_disconnect(self.poS_id)
+ if self.ae_id:
+ self.dataPlot.mpl_disconnect(self.ae_id)
+ self.poS_id = None
+ self.ae_id = None
+
+ def finishWaveformDataPlot(self):
+ self.connectWFplotEvents()
self.auto_pick.setEnabled(True)
self.z_action.setEnabled(True)
self.e_action.setEnabled(True)
@@ -704,12 +949,33 @@ class MainWindow(QMainWindow):
self.openautopicksaction.setEnabled(True)
self.loadpilotevent.setEnabled(True)
self.saveEventAction.setEnabled(True)
- if ans:
- self.plotWaveformData()
- return ans
- else:
- return ans
+ event = self.getCurrentEvent()
+ if event.picks:
+ self.picks = event.picks
+ self.drawPicks(picktype='manual')
+ if event.autopicks:
+ self.autopicks = event.autopicks
+ self.drawPicks(picktype='auto')
+ self.draw()
+ def clearWaveformDataPlot(self):
+ self.disconnectWFplotEvents()
+ self.dataPlot.getAxes().cla()
+ self.auto_pick.setEnabled(False)
+ self.z_action.setEnabled(False)
+ self.e_action.setEnabled(False)
+ self.n_action.setEnabled(False)
+ self.openmanualpicksaction.setEnabled(False)
+ self.openautopicksaction.setEnabled(False)
+ self.loadpilotevent.setEnabled(False)
+ self.saveEventAction.setEnabled(False)
+ self.draw()
+
+ def plotWaveformDataThread(self):
+ wfp_thread = Thread(self, self.plotWaveformData, progressText='Plotting waveform data...')
+ wfp_thread.finished.connect(self.finishWaveformDataPlot)
+ wfp_thread.start()
+
def plotWaveformData(self):
zne_text = {'Z': 'vertical', 'N': 'north-south', 'E': 'east-west'}
comp = self.getComponent()
@@ -718,7 +984,6 @@ class MainWindow(QMainWindow):
wfst = self.get_data().getWFData().select(component=comp)
wfst += self.get_data().getWFData().select(component=alter_comp)
self.getPlotWidget().plotWFData(wfdata=wfst, title=title, mapping=False)
- self.draw()
plotDict = self.getPlotWidget().getPlotDict()
pos = plotDict.keys()
labels = [plotDict[n][0] for n in pos]
@@ -726,19 +991,19 @@ class MainWindow(QMainWindow):
def plotZ(self):
self.setComponent('Z')
- self.plotWaveformData()
+ self.plotWaveformDataThread()
self.drawPicks()
self.draw()
def plotN(self):
self.setComponent('N')
- self.plotWaveformData()
+ self.plotWaveformDataThread()
self.drawPicks()
self.draw()
def plotE(self):
self.setComponent('E')
- self.plotWaveformData()
+ self.plotWaveformDataThread()
self.drawPicks()
self.draw()
@@ -924,8 +1189,10 @@ class MainWindow(QMainWindow):
def updatePicks(self, type='manual'):
picks = picksdict_from_picks(evt=self.get_data(type).get_evt_data())
if type == 'manual':
+ self.getCurrentEvent().addPicks(picks)
self.picks.update(picks)
elif type == 'auto':
+ self.getCurrentEvent().addAutopicks(picks)
self.autopicks.update(picks)
self.check4Comparison()
@@ -937,7 +1204,7 @@ class MainWindow(QMainWindow):
return
# plotting picks
plotID = self.getStationID(station)
- if not plotID:
+ if plotID is None:
return
ax = self.getPlotWidget().axes
ylims = np.array([-.5, +.5]) + plotID
@@ -1038,14 +1305,153 @@ class MainWindow(QMainWindow):
self.get_data().applyEVTData(lt.read_location(locpath), type='event')
self.get_data().applyEVTData(self.calc_magnitude(), type='event')
- def show_array_map(self):
+ def init_array_tab(self):
+ self.metadata_widget = QWidget(self)
+ grid_layout = QGridLayout()
+ grid_layout.setColumnStretch(0, 1)
+ grid_layout.setColumnStretch(2, 1)
+ grid_layout.setRowStretch(0, 1)
+ grid_layout.setRowStretch(3, 1)
+
+ label = QLabel('No inventory set...')
+ new_inv_button = QPushButton('Set &inventory file')
+ new_inv_button.clicked.connect(self.get_metadata)
+
+ grid_layout.addWidget(label, 1, 1)
+ grid_layout.addWidget(new_inv_button, 2, 1)
+
+ self.metadata_widget.setLayout(grid_layout)
+ self.array_layout.addWidget(self.metadata_widget)
+
+ def init_array_map(self, index=1):
if not self.array_map:
self.get_metadata()
if not self.metadata:
return
- self.array_map = map_projection(self)
+ self.array_map = map_projection(self)
+ self.array_layout.removeWidget(self.metadata_widget)
+ self.array_layout.addWidget(self.array_map)
+ self.tabs.setCurrentIndex(index)
+ self.refresh_array_map()
+
+ def refresh_array_map(self):
+ if not self.array_map:
+ return
+ # refresh with new picks here!!!
+ self.array_map.refresh_drawings(self.picks)
+ self._eventChanged[1] = False
+
+ def init_event_table(self, index=2):
+ def set_enabled(item, enabled=True, checkable=False):
+ if enabled and not checkable:
+ item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable)
+ elif enabled and checkable:
+ item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsSelectable)
+ else:
+ item.setFlags(QtCore.Qt.ItemIsSelectable)
+
+ if self.project:
+ eventlist = self.project.eventlist
else:
- self.array_map.show()
+ eventlist = []
+
+ def cell_changed(row=None, column=None):
+ table = self.project._table
+ event = self.project.getEventFromPath(table[row][0].text())
+ if column == 3 or column == 4:
+ #toggle checked states (exclusive)
+ item_ref = table[row][3]
+ item_test = table[row][4]
+ if column == 3 and item_ref.checkState():
+ item_test.setCheckState(QtCore.Qt.Unchecked)
+ event.setRefEvent(True)
+ elif column == 3 and not item_ref.checkState():
+ event.setRefEvent(False)
+ elif column == 4 and item_test.checkState():
+ item_ref.setCheckState(QtCore.Qt.Unchecked)
+ event.setTestEvent(True)
+ elif column == 4 and not item_test.checkState():
+ event.setTestEvent(False)
+ self.fill_eventbox()
+ elif column == 5:
+ #update event notes
+ notes = table[row][5].text()
+ event.addNotes(notes)
+ self.fill_eventbox()
+
+ if hasattr(self, 'qtl'):
+ self.events_layout.removeWidget(self.qtl)
+ self.qtl = QtGui.QTableWidget()
+ self.qtl.setColumnCount(6)
+ self.qtl.setRowCount(len(eventlist))
+ self.qtl.setHorizontalHeaderLabels(['Event', '[N] MP',
+ '[N] AP', 'Reference',
+ 'Test Set', 'Notes'])
+
+ self.project._table = []
+ for index, event in enumerate(eventlist):
+ event_npicks = 0
+ event_nautopicks = 0
+ if event.picks:
+ event_npicks = len(event.picks)
+ if event.autopicks:
+ event_nautopicks = len(event.autopicks)
+ item_path = QtGui.QTableWidgetItem()
+ item_nmp = QtGui.QTableWidgetItem(str(event_npicks))
+ item_nmp.setIcon(self.manupicksicon_small)
+ item_nap = QtGui.QTableWidgetItem(str(event_nautopicks))
+ item_nap.setIcon(self.autopicksicon_small)
+ item_ref = QtGui.QTableWidgetItem()
+ item_test = QtGui.QTableWidgetItem()
+ item_notes = QtGui.QTableWidgetItem()
+
+ item_ref.setBackground(QtGui.QColor(200, 210, 230, 255))
+ item_test.setBackground(QtGui.QColor(200, 230, 200, 255))
+ item_path.setText(event.path)
+ item_notes.setText(event.notes)
+ set_enabled(item_path, True, False)
+ set_enabled(item_nmp, True, False)
+ set_enabled(item_nap, True, False)
+ if event.picks:
+ set_enabled(item_ref, True, True)
+ set_enabled(item_test, True, True)
+ else:
+ set_enabled(item_ref, False, True)
+ set_enabled(item_test, False, True)
+
+ if event.isRefEvent():
+ item_ref.setCheckState(QtCore.Qt.Checked)
+ else:
+ item_ref.setCheckState(QtCore.Qt.Unchecked)
+ if event.isTestEvent():
+ item_test.setCheckState(QtCore.Qt.Checked)
+ else:
+ item_test.setCheckState(QtCore.Qt.Unchecked)
+
+ column=[item_path, item_nmp, item_nap, item_ref, item_test, item_notes]
+ self.project._table.append(column)
+
+ for r_index, row in enumerate(self.project._table):
+ for c_index, item in enumerate(row):
+ self.qtl.setItem(r_index, c_index, item)
+
+ header = self.qtl.horizontalHeader()
+ header.setResizeMode(QtGui.QHeaderView.ResizeToContents)
+ header.setStretchLastSection(True)
+ self.qtl.cellChanged[int, int].connect(cell_changed)
+
+ self.events_layout.addWidget(self.qtl)
+ self.tabs.setCurrentIndex(index)
+
+ def read_metadata_thread(self, fninv):
+ self.rm_thread = Thread(self, read_metadata, arg=fninv, progressText='Reading metadata...')
+ self.rm_thread.finished.connect(self.set_metadata)
+ self.rm_thread.start()
+
+ def set_metadata(self):
+ self.metadata = self.rm_thread.data
+ self.project.metadata = self.rm_thread.data
+ self.init_array_map()
def get_metadata(self):
def set_inv(settings):
@@ -1062,7 +1468,11 @@ class MainWindow(QMainWindow):
if ans == QMessageBox.Yes:
settings.setValue("inventoryFile", fninv)
settings.sync()
- self.metadata = read_metadata(fninv)
+ self.read_metadata_thread(fninv)
+ return True
+
+ if hasattr(self.project, 'metadata'):
+ self.metadata = self.project.metadata
return True
settings = QSettings()
@@ -1081,7 +1491,7 @@ class MainWindow(QMainWindow):
if not set_inv(settings):
return None
else:
- self.metadata = read_metadata(fninv)
+ self.read_metadata_thread(fninv)
def calc_magnitude(self, type='ML'):
self.get_metadata()
@@ -1142,7 +1552,7 @@ class MainWindow(QMainWindow):
self.setWindowTitle(
"PyLoT - processing event %s[*]" % self.get_data().getID())
elif self.get_data().isNew():
- self.setWindowTitle("PyLoT - New event [*]")
+ self.setWindowTitle("PyLoT - New project [*]")
else:
self.setWindowTitle(
"PyLoT - seismic processing the python way[*]")
@@ -1164,7 +1574,62 @@ class MainWindow(QMainWindow):
self.data = Data(self, evtdata=event)
self.setDirty(True)
+ def createNewProject(self, exists=False):
+ if self.okToContinue():
+ dlg = QFileDialog()
+ fnm = dlg.getSaveFileName(self, 'Create a new project file...', filter='Pylot project (*.plp)')
+ filename = fnm[0]
+ if not len(fnm[0]):
+ return
+ if not filename.split('.')[-1] == 'plp':
+ filename = fnm[0] + '.plp'
+ if not exists:
+ self.project = Project()
+ self.init_events(new=True)
+ self.project.save(filename)
+ return True
+
+ def loadProject(self):
+ if self.project:
+ if self.project.dirty:
+ qmb = QMessageBox(icon=QMessageBox.Question, text='Save changes in current project?')
+ qmb.setStandardButtons(QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
+ qmb.setDefaultButton(QMessageBox.Yes)
+ if qmb.exec_() == 16384:
+ self.saveProject()
+ elif qmb.exec_() == 65536:
+ pass
+ elif qmb.exec_() == 4194304:
+ return
+ dlg = QFileDialog()
+ fnm = dlg.getOpenFileName(self, 'Open project file...', filter='Pylot project (*.plp)')
+ if fnm[0]:
+ self.project = Project.load(fnm[0])
+ self.init_events(new=True)
+ if hasattr(self.project, 'metadata'):
+ self.init_array_map(index=0)
+
+ def saveProject(self):
+ if self.project:
+ if not self.project.location:
+ if not self.createNewProject(exists=True):
+ return
+ else:
+ self.project.save()
+ if not self.project.dirty:
+ qmb = QMessageBox(icon=QMessageBox.Information, text='Saved back project to file:\n{}'.format(self.project.location))
+ qmb.exec_()
+ return
+ else:
+ # if still dirty because saving failed
+ qmb = QMessageBox(icon=QMessageBox.Warning, text='Could not save back to original file.\n'
+ 'Choose new file')
+ qmb.setStandardButtons(QMessageBox.Ok)
+ qmb.exec_()
+ self.createNewProject(exists=True)
+
def draw(self):
+ self.fill_eventbox()
self.getPlotWidget().draw()
def setDirty(self, value):
@@ -1189,6 +1654,120 @@ class MainWindow(QMainWindow):
form.show()
+class Project(object):
+ '''
+ Pickable class containing information of a QtPyLoT project, like event lists and file locations.
+ '''
+ def __init__(self):
+ self.eventlist = []
+ self.location = None
+ self.dirty = False
+ self._table = None
+
+ def add_eventlist(self, eventlist):
+ if len(eventlist) == 0:
+ return
+ for item in eventlist:
+ event = Event(item)
+ if not event in self.eventlist:
+ self.eventlist.append(event)
+ self.setDirty()
+
+ def setDirty(self):
+ self.dirty = True
+
+ def setClean(self):
+ self.dirty = False
+
+ def getEventFromPath(self, path):
+ for event in self.eventlist:
+ if event.path == path:
+ return event
+
+ def save(self, filename=None):
+ '''
+ Save PyLoT Project to a file.
+ Can be loaded by using project.load(filename).
+ '''
+ try:
+ import cPickle
+ except ImportError:
+ import _pickle as cPickle
+
+ if filename:
+ self.location = filename
+ else:
+ filename = self.location
+
+ try:
+ outfile = open(filename, 'wb')
+ cPickle.dump(self, outfile, -1)
+ self.setClean()
+ except Exception as e:
+ print('Could not pickle PyLoT project. Reason: {}'.format(e))
+ self.setDirty()
+
+ @staticmethod
+ def load(filename):
+ try:
+ import cPickle
+ except ImportError:
+ import _pickle as cPickle
+ infile = open(filename, 'rb')
+ project = cPickle.load(infile)
+ print('Loaded %s' % filename)
+ return project
+
+
+class Event(object):
+ '''
+ Pickable class containing information on a single event.
+ '''
+ def __init__(self, path):
+ self.path = path
+ self.autopicks = None
+ self.picks = None
+ self.notes = None
+ self._testEvent = False
+ self._refEvent = False
+
+ def addPicks(self, picks):
+ self.picks = picks
+
+ def addAutopicks(self, autopicks):
+ self.autopicks = autopicks
+
+ def addNotes(self, notes):
+ self.notes = notes
+
+ def clearNotes(self):
+ self.notes = None
+
+ def isRefEvent(self):
+ return self._refEvent
+
+ def isTestEvent(self):
+ return self._testEvent
+
+ def setRefEvent(self, bool):
+ self._refEvent = bool
+ if bool: self._testEvent = False
+
+ def setTestEvent(self, bool):
+ self._testEvent = bool
+ if bool: self._refEvent = False
+
+
+class getExistingDirectories(QFileDialog):
+ def __init__(self, *args):
+ super(getExistingDirectories, self).__init__(*args)
+ self.setOption(self.DontUseNativeDialog, True)
+ self.setFileMode(self.Directory)
+ self.setOption(self.ShowDirsOnly, True)
+ self.findChildren(QListView)[0].setSelectionMode(QAbstractItemView.ExtendedSelection)
+ self.findChildren(QTreeView)[0].setSelectionMode(QAbstractItemView.ExtendedSelection)
+
+
def create_window():
app_created = False
app = QCoreApplication.instance()
diff --git a/icons.qrc b/icons.qrc
index 4c39b449..40fca409 100644
--- a/icons.qrc
+++ b/icons.qrc
@@ -4,6 +4,8 @@
icons/pylot.png
icons/manupicsicon.png
icons/autopicsicon.png
+ icons/manupicksicon_small.png
+ icons/autopicksicon_small.png
icons/autopick_button.png
icons/locactionicon.png
icons/compare_button.png
diff --git a/icons/autopicksicon_small.png b/icons/autopicksicon_small.png
new file mode 100644
index 00000000..2c626102
Binary files /dev/null and b/icons/autopicksicon_small.png differ
diff --git a/icons/manupicksicon_small.png b/icons/manupicksicon_small.png
new file mode 100644
index 00000000..05266976
Binary files /dev/null and b/icons/manupicksicon_small.png differ
diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION
index 0fec275e..c6a4ec44 100644
--- a/pylot/RELEASE-VERSION
+++ b/pylot/RELEASE-VERSION
@@ -1 +1 @@
-de38-dirty
+6b7d-dirty
diff --git a/pylot/core/util/map_projection.py b/pylot/core/util/map_projection.py
index c235d2d1..53fc8ac7 100644
--- a/pylot/core/util/map_projection.py
+++ b/pylot/core/util/map_projection.py
@@ -12,10 +12,17 @@ from pylot.core.util.widgets import PickDlg
plt.interactive(False)
class map_projection(QtGui.QWidget):
- def __init__(self, mainwindow):
+ def __init__(self, mainwindow, figure=None):
+ '''
+ :param: picked, can be False, auto, manual
+ :value: str
+ '''
QtGui.QWidget.__init__(self)
self.pyl_mainwindow = mainwindow
self.parser = mainwindow.metadata[1]
+ self.picks = None
+ self.picks_dict = None
+ self.figure = figure
self.init_graphics()
self.init_stations()
self.init_lat_lon_dimensions()
@@ -24,7 +31,7 @@ class map_projection(QtGui.QWidget):
self.init_x_y_dimensions()
self.connectSignals()
self.draw_everything()
- self.show()
+ #self.show()
def onpick(self, event):
ind = event.ind
@@ -57,7 +64,7 @@ class map_projection(QtGui.QWidget):
print('Could not generate Plot for station {st}.\n{er}'.format(st=station, er=e))
def connectSignals(self):
- self.combobox.currentIndexChanged.connect(self.refresh_drawings)
+ self.comboBox_phase.currentIndexChanged.connect(self._refresh_drawings)
def init_graphics(self):
self.main_box = QtGui.QVBoxLayout()
@@ -66,20 +73,29 @@ class map_projection(QtGui.QWidget):
self.top_row = QtGui.QHBoxLayout()
self.main_box.addLayout(self.top_row)
- self.combobox = QtGui.QComboBox()
- self.combobox.insertItem(0, 'P')
- self.combobox.insertItem(1, 'S')
- self.top_row.addWidget(QtGui.QLabel('Select a phase: '))
- self.top_row.addWidget(self.combobox)
+ self.comboBox_phase = QtGui.QComboBox()
+ self.comboBox_phase.insertItem(0, 'P')
+ self.comboBox_phase.insertItem(1, 'S')
+
+ # self.comboBox_am = QtGui.QComboBox()
+ # self.comboBox_am.insertItem(0, 'auto')
+ # self.comboBox_am.insertItem(1, 'manual')
- fig = plt.figure()
+ self.top_row.addWidget(QtGui.QLabel('Select a phase: '))
+ self.top_row.addWidget(self.comboBox_phase)
+ self.top_row.setStretch(1,1) #set stretch of item 1 to 1
+
+ if not self.figure:
+ fig = plt.figure()
+ else:
+ fig = self.figure
self.main_ax = fig.add_subplot(111)
self.canvas = fig.canvas
self.main_box.addWidget(self.canvas)
self.toolbar = NavigationToolbar(self.canvas, self)
self.main_box.addWidget(self.toolbar)
-
+
def init_stations(self):
def get_station_names_lat_lon(parser):
station_names=[]
@@ -99,12 +115,12 @@ class map_projection(QtGui.QWidget):
self.lon = lon
def init_picks(self):
- phase = self.combobox.currentText()
+ phase = self.comboBox_phase.currentText()
def get_picks(station_names):
picks=[]
for station in station_names:
try:
- picks.append(self.pyl_mainwindow.autopicks[station][phase]['mpp'])
+ picks.append(self.picks_dict[station][phase]['mpp'])
except:
picks.append(np.nan)
return picks
@@ -216,30 +232,47 @@ class map_projection(QtGui.QWidget):
cbar.set_label(label)
return cbar
- def refresh_drawings(self):
+ def refresh_drawings(self, picks=None):
+ self.picks_dict = picks
self.remove_drawings()
self.draw_everything()
-
+
+ def _refresh_drawings(self):
+ self.remove_drawings()
+ self.draw_everything()
+
def draw_everything(self):
- self.init_picks()
- self.init_picks_active()
- self.init_stations_active()
- self.init_picksgrid()
- self.draw_contour_filled()
+ if self.picks_dict:
+ self.init_picks()
+ self.init_picks_active()
+ self.init_stations_active()
+ self.init_picksgrid()
+ self.draw_contour_filled()
self.scatter_all_stations()
- self.scatter_picked_stations()
+ if self.picks_dict:
+ self.scatter_picked_stations()
+ self.cbar = self.add_cbar(label='Time relative to first onset [s]')
+ self.comboBox_phase.setEnabled(True)
+ else:
+ self.comboBox_phase.setEnabled(False)
self.annotate_ax()
- self.cbar = self.add_cbar(label='Time relative to first onset [s]')
self.canvas.draw()
def remove_drawings(self):
- self.sc_picked.remove()
- self.sc.remove()
- self.cbar.remove()
- self.remove_annotations()
+ if hasattr(self, 'sc_picked'):
+ self.sc_picked.remove()
+ del(self.sc_picked)
+ if hasattr(self, 'cbar'):
+ self.cbar.remove()
+ del(self.cbar)
+ if hasattr(self, 'contourf'):
+ self.remove_contourf()
+ del(self.contourf)
+ if hasattr(self, 'cid'):
+ self.canvas.mpl_disconnect(self.cid)
+ del(self.cid)
+ self.sc.remove()
self.legend.remove()
- self.remove_contourf()
- self.canvas.mpl_disconnect(self.cid)
self.canvas.draw()
def remove_contourf(self):
diff --git a/pylot/core/util/thread.py b/pylot/core/util/thread.py
index 038aef8d..fc76081b 100644
--- a/pylot/core/util/thread.py
+++ b/pylot/core/util/thread.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import sys
-from PySide.QtCore import QThread, Signal
+from PySide.QtCore import QThread, Signal, Qt
+from PySide.QtGui import QDialog, QProgressBar, QLabel, QVBoxLayout
class AutoPickThread(QThread):
@@ -35,3 +36,42 @@ class AutoPickThread(QThread):
def flush(self):
pass
+
+
+class Thread(QThread):
+ def __init__(self, parent, func, arg=None, progressText=None):
+ QThread.__init__(self, parent)
+ self.func = func
+ self.arg = arg
+ self.progressText = progressText
+ self.pbdlg = None
+ self.finished.connect(self.hideProgressbar)
+ self.showProgressbar()
+
+ def run(self):
+ if self.arg:
+ self.data = self.func(self.arg)
+ else:
+ self.data = self.func()
+
+ def __del__(self):
+ self.wait()
+
+ def showProgressbar(self):
+ if self.progressText:
+ self.pbdlg = QDialog(self.parent())
+ self.pbdlg.setModal(True)
+ vl = QVBoxLayout()
+ pb = QProgressBar()
+ pb.setRange(0, 0)
+ vl.addWidget(pb)
+ vl.addWidget(QLabel(self.progressText))
+ self.pbdlg.setLayout(vl)
+ self.pbdlg.setWindowFlags(Qt.SplashScreen)
+ self.pbdlg.show()
+
+ def hideProgressbar(self):
+ if self.pbdlg:
+ self.pbdlg.hide()
+
+
diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py
index 969ad945..8ad102df 100644
--- a/pylot/core/util/widgets.py
+++ b/pylot/core/util/widgets.py
@@ -397,7 +397,7 @@ class WaveformWidget(FigureCanvas):
self.setParent(parent)
self.figure = Figure()
self.figure.set_facecolor((.92, .92, .92))
- # attribute plotdict is an dictionary connecting position and a name
+ # attribute plotdict is a dictionary connecting position and a name
self.plotdict = dict()
# create axes
self.axes = self.figure.add_subplot(111)