#!/usr/bin/python
from PyQt5 import QtGui, QtCore, QtWidgets
import sys
import time # noqa
import pyqtgraph as pg
from kite.qt_utils import loadUi, validateFilename, SceneLog
from kite.scene import Scene
from .scene_model import SceneModel
from .tab_scene import KiteScene
from .tab_quadtree import KiteQuadtree # noqa
from .tab_covariance import KiteCovariance # noqa
from .base import get_resource
[docs]class Spool(QtWidgets.QApplication):
def __init__(self, scene=None, import_file=None, load_file=None):
QtWidgets.QApplication.__init__(self, ['Spool'])
# self.setStyle('plastique')
splash_img = QtGui.QPixmap(get_resource('spool_splash.png'))\
.scaled(QtCore.QSize(400, 250), QtCore.Qt.KeepAspectRatio)
self.splash = QtWidgets.QSplashScreen(
splash_img, QtCore.Qt.WindowStaysOnTopHint)
self.updateSplashMessage('Scene')
self.splash.show()
self.processEvents()
self.spool_win = SpoolMainWindow()
self.spool_win.sigLoadingModule.connect(self.updateSplashMessage)
self.spool_win.actionExit.triggered.connect(self.exit)
self.aboutToQuit.connect(self.spool_win.model.worker_thread.quit,
type=QtCore.Qt.QueuedConnection)
self.aboutToQuit.connect(self.spool_win.model.deleteLater)
self.aboutToQuit.connect(self.splash.deleteLater)
self.aboutToQuit.connect(self.deleteLater)
if scene is not None:
self.addScene(scene)
elif import_file is not None:
self.importScene(import_file)
elif load_file is not None:
self.loadScene(load_file)
self.splash.finish(self.spool_win)
self.spool_win.show()
[docs] @QtCore.pyqtSlot(str)
def updateSplashMessage(self, msg=''):
self.splash.showMessage(
'Loading %s ...' % msg.title(), QtCore.Qt.AlignBottom)
[docs] def addScene(self, scene):
self.spool_win.addScene(scene)
[docs] def importScene(self, filename):
self.spool_win.model.importFile(filename)
[docs] def loadScene(self, filename):
self.spool_win.model.loadFile(filename)
class SpoolMainWindow(QtWidgets.QMainWindow):
VIEWS = [KiteScene, KiteQuadtree, KiteCovariance]
sigImportFile = QtCore.pyqtSignal(str)
sigLoadFile = QtCore.pyqtSignal(str)
sigLoadConfig = QtCore.pyqtSignal(str)
sigExportWeightMatrix = QtCore.pyqtSignal(str)
sigLoadingModule = QtCore.pyqtSignal(str)
def __init__(self, *args, **kwargs):
QtWidgets.QMainWindow.__init__(self, *args, **kwargs)
loadUi(get_resource('spool.ui'), baseinstance=self)
self.views = []
self.active_view = None
self.ptree = KiteParameterTree(showHeader=False)
self.ptree_dock = QtWidgets.QDockWidget('Parameters', self)
self.ptree_dock.setFeatures(
QtWidgets.QDockWidget.DockWidgetFloatable |
QtWidgets.QDockWidget.DockWidgetMovable)
self.ptree_dock.setWidget(self.ptree)
self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.ptree_dock)
self.model = SceneModel()
self.model.sigSceneModelChanged.connect(
self.buildViews)
self.sigLoadFile.connect(
self.model.loadFile)
self.sigImportFile.connect(
self.model.importFile)
self.sigLoadConfig.connect(
self.model.loadConfig)
self.sigExportWeightMatrix.connect(
self.model.exportWeightMatrix)
self.actionSave_config.triggered.connect(
self.onSaveConfig)
self.actionSave_scene.triggered.connect(
self.onSaveScene)
self.actionLoad_config.triggered.connect(
self.onLoadConfig)
self.actionLoad_scene.triggered.connect(
self.onOpenScene)
self.actionImport_scene.triggered.connect(
self.onImportScene)
self.actionExport_quadtree.triggered.connect(
self.onExportQuadtree)
self.actionExport_weights.triggered.connect(
self.onExportWeightMatrix)
self.actionAbout_Spool.triggered.connect(
self.aboutDialog().show)
self.actionAbout_Qt5.triggered.connect(
lambda: QtGui.QMessageBox.aboutQt(self))
self.actionHelp.triggered.connect(
lambda: QtGui.QDesktopServices.openUrl('https://pyrocko.org'))
self.log = SceneLog(self, self.model)
self.actionLog.triggered.connect(
self.log.show)
self.progress = QtWidgets.QProgressDialog('', None, 0, 0, self)
self.progress.setValue(0)
self.progress.closeEvent = lambda ev: ev.ignore()
self.progress.setMinimumWidth(400)
self.progress.setWindowTitle('Processing...')
self.progress.reset()
self.progress_timer = None
def aboutDialog(self):
self._about = QtGui.QDialog(self)
loadUi(get_resource('about.ui'), baseinstance=self._about)
return self._about
def addScene(self, scene):
self.model.setScene(scene)
self.buildViews()
def buildViews(self):
title = self.model.scene.meta.filename or 'Untitled'
self.setWindowTitle('Spool - %s' % title)
if self.model.scene is None or self.tabs.count() != 0:
return
for v in self.VIEWS:
self.addView(v)
self.model.sigProcessingStarted.connect(
self.processingStarted,
type=QtCore.Qt.QueuedConnection)
self.model.sigProcessingFinished.connect(
self.processingFinished,
type=QtCore.Qt.QueuedConnection)
self.tabs.currentChanged.connect(self.activateView)
self.activateView(0)
def addView(self, view):
self.sigLoadingModule.emit(view.title)
view = view(self)
QtCore.QCoreApplication.processEvents()
self.tabs.addTab(view, view.title)
QtCore.QCoreApplication.processEvents()
if hasattr(view, 'parameters'):
for parameter in view.parameters:
self.ptree.addParameters(parameter)
self.views.append(view)
@QtCore.pyqtSlot(int)
def activateView(self, index):
if self.active_view is not None:
self.active_view.deactivateView()
self.active_view = self.views[index]
self.active_view.activateView()
@QtCore.pyqtSlot(str)
def processingStarted(self, text):
quadtree = self.model.quadtree
covariance = self.model.covariance
ncombinations = quadtree.nleaves*(quadtree.nleaves+1)/2
self.progress.setLabelText(text)
self.progress.setMaximum(ncombinations)
self.progress.setValue(0)
@QtCore.pyqtSlot()
def updateProgress():
self.progress.setValue(covariance.finished_combinations)
self.progress_timer = QtCore.QTimer()
self.progress_timer.timeout.connect(updateProgress)
self.progress_timer.start(250)
self.progress.show()
@QtCore.pyqtSlot()
def processingFinished(self):
self.progress.reset()
if self.progress_timer is not None:
self.progress_timer.stop()
self.progress_timer = None
def onSaveConfig(self):
filename, _ = QtWidgets.QFileDialog.getSaveFileName(
filter='YAML file *.yml (*.yml)', caption='Save scene YAML config')
if not validateFilename(filename):
return
self.model.scene.saveConfig(filename)
QtWidgets.QMessageBox.information(
self, 'Scene config saved',
'Scene config successfuly saved!'
'<p style="font-family: monospace;">%s'
'</p>' % filename)
def onSaveScene(self):
filename, _ = QtWidgets.QFileDialog.getSaveFileName(
filter='YAML *.yml and NumPy container *.npz (*.yml *.npz)',
caption='Save scene')
if not validateFilename(filename):
return
self.model.scene.save(filename)
QtWidgets.QMessageBox.information(
self, 'Scene saved',
'Scene successfuly saved!'
'<p style="font-family: monospace;">%s'
'</p>' % filename)
def onLoadConfig(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
filter='YAML file *.yml (*.yml)', caption='Load scene YAML config')
if not validateFilename(filename):
return
self.sigLoadConfig.emit(filename)
def onOpenScene(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
filter='YAML *.yml and NumPy container *.npz (*.yml *.npz)',
caption='Load kite scene')
if not validateFilename(filename):
return
self.model.loadFile(filename)
def onImportScene(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
self,
filter='Supported Formats, *.grd, *.geo, *unw*, *.mat '
'(*.grd,*.geo,*unw*,*.mat);;'
'GMT5SAR Scene, *.grd (*.grd);;'
'ISCE Scene, *unw* (*unw*);;Gamma Scene *.geo (*.geo);;'
'Matlab Container, *.mat (*.mat);;Any File * (*)',
caption='Import scene to spool')
if not validateFilename(filename):
return
self.sigImportFile.emit(filename)
def onExportQuadtree(self):
filename, flt = QtWidgets.QFileDialog.getSaveFileName(
filter='GeoJSON (*.geojson *.json);;CSV File *.csv (*.csv)',
caption='Export Quadtree')
if not validateFilename(filename):
return
if flt == 'GeoJSON (*.geojson *.json)':
if not filename.endswith('.geojson') and \
not filename.endswith('.json'):
filename += '.geojson'
self.model.quadtree.export_geojson(filename)
elif flt == 'CSV File *.csv (*.csv)':
if not filename.endswith('.csv'):
filename += '.geojson'
self.model.quadtree.export_csv(filename)
else:
raise ValueError('unknown filter')
def onExportWeightMatrix(self):
filename, _ = QtWidgets.QFileDialog.getSaveFileName(
filter='Text File *.txt (*.txt)',
caption='Export Covariance Weights',)
if not validateFilename(filename):
return
self.sigExportWeightMatrix.emit(filename)
def exit(self):
pass
class KiteParameterTree(pg.parametertree.ParameterTree):
pass
[docs]def spool(*args, **kwargs):
spool_app = Spool(*args, **kwargs)
spool_app.exec_()
spool_app.quit()
__all__ = ['Spool', 'spool']
if __name__ == '__main__':
from kite.scene import SceneSynTest
if len(sys.argv) > 1:
sc = Scene.load(sys.argv[1])
else:
sc = SceneSynTest.createGauss()
Spool(scene=sc)