[segyio] 44/376: Refactoring. Modularizing viewer

Jørgen Kvalsvik jokva-guest at moszumanska.debian.org
Wed Sep 20 08:04:05 UTC 2017


This is an automated email from the git hooks/post-receive script.

jokva-guest pushed a commit to branch debian
in repository segyio.

commit 7f518604291b1423a922beb70d2fac7a1a22d3cb
Author: Thorvald Johannessen <thorvjo at statoil.com>
Date:   Fri Oct 7 11:39:22 2016 +0200

    Refactoring. Modularizing viewer
    
    Classes are renamed and splitted up into seperate files, for easier use outside the example-application segyviewer. I.e to plot slices without QT.
---
 applications/segyviewer.py           | 385 +++++++---------------------------
 examples/CMakeLists.txt              |   2 +-
 examples/segyviewer.py               | 387 -----------------------------------
 python/CMakeLists.txt                |   3 +-
 python/segyview/CMakeLists.txt       |   8 +
 python/segyview/__init__.py          |  10 +
 python/segyview/linenavigationbar.py |  50 +++++
 python/segyview/segyplot.py          | 117 +++++++++++
 python/segyview/slicewidget.py       |  78 +++++++
 9 files changed, 341 insertions(+), 699 deletions(-)

diff --git a/applications/segyviewer.py b/applications/segyviewer.py
index 1ed4170..edb2e07 100755
--- a/applications/segyviewer.py
+++ b/applications/segyviewer.py
@@ -1,12 +1,11 @@
 #!/usr/bin/env python
 
-import segyio
+import sys
 
-from pylab import *
+import numpy as np
+import segyio
 from PyQt4 import QtGui, QtCore
-from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
-from matplotlib.figure import Figure
-import matplotlib.patches as patches
+from segyview import *
 
 
 class LineSelectionMonitor(QtCore.QObject):
@@ -26,6 +25,7 @@ class LineSelectionMonitor(QtCore.QObject):
     def depthUpdated(self, new_index):
         self.depthChanged.emit(new_index)
 
+
 class ColorMapMonitor(QtCore.QObject):
     cmap_changed = QtCore.pyqtSignal(str)
 
@@ -36,351 +36,118 @@ class ColorMapMonitor(QtCore.QObject):
         self.cmap_changed.emit(str(value))
 
 
-class PlaneCanvas(FigureCanvas):
-    """
-    Generic plot canvas for all plane views
-    """
-    indexChanged = QtCore.pyqtSignal(int)
-
-    def __init__(self, planes, indexes, dataset_title, cmap, x_axis_indexes=None, y_axis_indexes=None,
-                 display_horizontal_indicator=False,
-                 display_vertical_indicator=False, parent=None, width=800, height=100, dpi=20, v_min_max=None):
-
-        self.fig = Figure(figsize=(width, height), dpi=dpi, facecolor='white')
-        FigureCanvas.__init__(self, self.fig)
-
-        self.planes = planes
-        self.indexes = indexes
-
-        if x_axis_indexes:
-            self.x_axis_name, self.x_axis_indexes = x_axis_indexes
-        else:
-            self.x_axis_indexes = None
-
-        if y_axis_indexes:
-            self.y_axis_name, self.y_axis_indexes = y_axis_indexes
-        else:
-            self.y_axis_indexes = None
-
-
-        self.plane_height = len(self.planes[self.indexes[0]][0])
-        self.plane_width = len(self.planes[self.indexes[0]][:])
-
-        self.dataset_title = dataset_title
-
-        self.setParent(parent)
-
-        self.axes = self.fig.add_subplot(111)
-        self.axes.tick_params(axis='both', labelsize=30)
-
-        if self.x_axis_indexes:
-            def x_axis_label_formatter(val, position):
-                if val >= 0 and val < len(self.x_axis_indexes):
-                    return self.x_axis_indexes[int(val)]
-                return ''
-            self.axes.set_xlabel(self.x_axis_name, fontsize=30)
-            self.axes.get_xaxis().set_major_formatter(FuncFormatter(x_axis_label_formatter))
-            self.axes.get_xaxis().set_major_locator(MaxNLocator(20))    # max 20 ticks are shown
-
-
-        if self.y_axis_indexes:
-            def y_axis_label_formatter(val, position):
-                if val >= 0 and val < len(self.y_axis_indexes):
-                    return self.y_axis_indexes[int(val)]
-                return ''
-
-            self.axes.set_ylabel(self.y_axis_name, fontsize=30)
-            self.axes.get_yaxis().set_major_formatter(FuncFormatter(y_axis_label_formatter))
-            self.axes.get_yaxis().set_major_locator(MaxNLocator(20))    # max 20 ticks are shown
-
-        self.cmap = cmap
-
-        self.im = self.axes.imshow(planes[indexes[0]].T,
-                                   interpolation="nearest",
-                                   aspect="auto",
-                                   cmap=self.cmap,
-                                   vmin=v_min_max[0],
-                                   vmax=v_min_max[1])
-
-        self.current_index = 0
-
-        # connecting matplotlib mouse signals
-        self.mpl_connect('motion_notify_event', self.mouse_moved)
-        self.mpl_connect('button_press_event', self.mouse_clicked)
-        self.mpl_connect('axes_leave_event', self.mouse_left)
-
-        if display_vertical_indicator:
-            self.verdical_indicator_rect = self.axes.add_patch(
-                patches.Rectangle(
-                    (-0.5, -0.5),
-                    1,
-                    self.plane_height,
-                    fill=False,
-                    alpha=1,
-                    color='black',
-                    linestyle='--',
-                    linewidth=2
-                )
-            )
-
-        if display_horizontal_indicator:
-            self.horizontal_indicator_rect = self.axes.add_patch(
-                patches.Rectangle(
-                    (-0.5, -0.5),
-                    self.plane_width,
-                    1,
-                    fill=False,
-                    alpha=1,
-                    color='black',
-                    linestyle='--',
-                    linewidth=2
-                )
-            )
-
-        self.disabled_overlay = self.axes.add_patch(
-            patches.Rectangle(
-                (-0.5, -0.5),  # (x,y)
-                len(self.planes[self.indexes[0]][0]),
-                len(self.planes[self.indexes[0]][0]),
-                alpha=0.5,
-                color='gray',
-                visible=False
-            )
-        )
-
-
-    def mouse_left(self, evt):
-        # for now do nothing
-        # self.set_vertical_line_indicator(self.current_index)
-        pass
-
-
-    def mouse_clicked(self, evt):
-        if evt.inaxes:
-            self.current_index = int(evt.xdata)
-            self.indexChanged.emit(self.x_axis_indexes[int(evt.xdata)])
-
-    def mouse_moved(self, evt):
-        # for now do nothing
-        # if evt.inaxes:
-        #   self.set_vertical_line_indicator(int(evt.xdata))
-        pass
-
-    def set_colormap(self, cmap):
-        self.cmap = cmap
-        self.im.set_cmap(cmap)
-        self.draw()
-
-    def update_image(self, index):
-        self.im.set_data(self.planes[index].T)
-        self.draw()
-
-    def set_vertical_line_indicator(self, line_index):
-        self.verdical_indicator_rect.set_x(self.x_axis_indexes.index(line_index) - 0.5)
-        self.draw()
-
-    def set_horizontal_line_indicator(self, line_index):
-        self.horizontal_indicator_rect.set_y(self.y_axis_indexes.index(line_index)-0.5)
-        self.draw()
-
-    def enable_overlay(self):
-        self.disabled_overlay.set_visible(True)
-        self.draw()
-
-    def disable_overlay(self):
-        self.disabled_overlay.set_visible(False)
-        self.draw()
-
-
-class PlotWidget(QtGui.QWidget):
-    """
-    Main widget holding the figure and slider
-    """
-
-    def __init__(self, planes, indexes, dataset_title, default_cmap='seismic',
-                 x_axis_indexes=None, y_axis_indexes=None,
-                 show_h_indicator=False, show_v_indicator=False, v_min_max=None):
-        super(PlotWidget, self).__init__()
-
-        self.planes = planes
-        self.indexes = indexes
-        self.dataset_title = dataset_title
-        self.default_cmap = default_cmap
-        self.show_h_indicator = show_h_indicator
-        self.show_v_indicator = show_v_indicator
-        self.setAutoFillBackground(True)
-        p = self.palette()
-        p.setColor(self.backgroundRole(), QtCore.Qt.white)
-        self.setPalette(p)
-
-        self.plotCanvas = PlaneCanvas(self.planes, self.indexes, self.dataset_title, self.default_cmap,
-                                      x_axis_indexes=x_axis_indexes, y_axis_indexes=y_axis_indexes,
-                                      display_horizontal_indicator=self.show_h_indicator,
-                                      display_vertical_indicator=self.show_v_indicator, v_min_max=v_min_max)
-
-        self.layout = QtGui.QVBoxLayout(self)
-        self.layout.addWidget(self.plotCanvas)
-
-    def set_cmap(self, action):
-        self.plotCanvas.set_colormap(str(action))
-
-    def set_vertical_line_indicator(self, line):
-        self.plotCanvas.set_vertical_line_indicator(line)
-
-    def set_horizontal_line_indicator(self, line):
-        self.plotCanvas.set_horizontal_line_indicator(line)
-
-
-class TopMenu(QtGui.QMenuBar):
-    def __init__(self, parent, colormap_monitor):
-        super(QtGui.QMenuBar, self).__init__(parent)
-
-        self.fileMenu = self.addMenu('&File')
-        exitAction = QtGui.QAction('&Exit', self)
-        self.fileMenu.addAction(exitAction)
-
-        self.viewMenu = self.addMenu('&View')
-        self.colormapMenu = self.viewMenu.addMenu("&Colormap")
-
-        self.colormap_monitor = colormap_monitor
-
-        self.colormap_monitor.cmap_changed.connect(self.set_selected_cmap)
+def configure_main_menu(menu, colormap_monitor, available_colormaps):
+    menu.fileMenu = menu.addMenu('&File')
+    exitAction = QtGui.QAction('&Exit', menu)
+    menu.fileMenu.addAction(exitAction)
 
-        def colormapChanger(color_map_name):
-            def performColorMapChange():
-                self.colormap_monitor.colormapUpdated(color_map_name)
-            return performColorMapChange
+    menu.viewMenu = menu.addMenu('&View')
+    menu.colormapMenu = menu.viewMenu.addMenu("&Colormap")
 
-        for item in ['seismic', 'spectral', 'RdGy', 'hot', 'jet', 'gray']:
-            action = self.colormapMenu.addAction(item)
-            action.setCheckable(True)
-            action.triggered.connect(colormapChanger(item))
-
-    def set_selected_cmap(self, cmap_name):
-        for item in self.colormapMenu.actions():
+    def set_selected_cmap(cmap_name):
+        for item in menu.colormapMenu.actions():
             item.setChecked(str(item.text()) == cmap_name)
 
+    colormap_monitor.cmap_changed.connect(set_selected_cmap)
 
-class LineSelector(QtGui.QWidget):
-    indexChanged = QtCore.pyqtSignal(int)
-
-    def __init__(self, parent, label, indexes, monitor_func):
-        super(QtGui.QWidget, self).__init__(parent)
-        self.label = label
-        self.indexes = indexes
-        self.monitor_func = monitor_func
-
-        self.layout = QtGui.QHBoxLayout()
-        self.slabel = QtGui.QLabel(self.label)
-        self.sbox = QtGui.QSpinBox(self)
-        self.sbox.setRange(self.indexes[0], self.indexes[-1])
-        self.sbox.valueChanged.connect(self.monitor_func)
-        self.layout.addWidget(self.slabel)
-        self.layout.addWidget(self.sbox)
-        self.setLayout(self.layout)
-
-    def index_changed(self, val):
-        self.indexChanged.emit(val)
+    def colormapChanger(color_map_name):
+        def performColorMapChange():
+            colormap_monitor.colormapUpdated(color_map_name)
 
-    def set_index(self, val):
-        self.sbox.blockSignals(True)
-        self.sbox.setValue(val)
-        self.sbox.blockSignals(False)
+        return performColorMapChange
 
+    for item in available_colormaps:
+        action = menu.colormapMenu.addAction(item)
+        action.setCheckable(True)
+        action.triggered.connect(colormapChanger(item))
 
-class ToolBar(QtGui.QToolBar):
-    def __init__(self, xline_indexes, iline_indexes, depth_indexes, line_selection_monitor):
-        super(ToolBar, self).__init__("")
-        self.xline_indexes = xline_indexes
-        self.iline_indexes = iline_indexes
-        self.depth_indexes = depth_indexes
-        self.line_selection_monitor = line_selection_monitor
 
-        # xline
-        self.xline_selector = LineSelector(self, "xline", self.xline_indexes, self.line_selection_monitor.xlineUpdated)
-        self.line_selection_monitor.xlineChanged.connect(self.xline_selector.set_index)
-        self.addWidget(self.xline_selector)
-
-        # iline
-        self.iline_selector = LineSelector(self, "iline", self.iline_indexes, self.line_selection_monitor.ilineUpdated)
-        self.line_selection_monitor.ilineChanged.connect(self.iline_selector.set_index)
-        self.addWidget(self.iline_selector)
-
-        # iline
-        self.depth_selector = LineSelector(self, "depth", self.depth_indexes, self.line_selection_monitor.depthUpdated)
-        self.addWidget(self.depth_selector)
-
-
-class AppWindow(QtGui.QMainWindow):
+class SegyViewer(QtGui.QMainWindow):
     def __init__(self, s):
         QtGui.QMainWindow.__init__(self)
         self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
-        self.setWindowTitle("Segy Viewer")
-        self.main_widget = QtGui.QWidget(self)
+        self.setWindowTitle("SegyViewer")
+        main_widget = QtGui.QWidget(self)
 
         # signal monitors
         colormap_monitor = ColorMapMonitor(self)
         line_monitor = LineSelectionMonitor(self)
 
-        self.setMenuBar(TopMenu(self, colormap_monitor))
-        self.addToolBar(ToolBar(s.xlines, s.ilines, range(s.samples), line_monitor))
+
+        # menus
+        available_colormaps = ['seismic', 'spectral', 'RdGy', 'hot', 'jet', 'gray']
+        configure_main_menu(self.menuBar(), colormap_monitor, available_colormaps)
+
+
+        self.addToolBar(LineNavigationBar(s.xlines, s.ilines, range(s.samples), line_monitor))
         self.statusBar()
 
-        depth_planes, min_max = read_traces_to_memory(s)
+        depth_slices, min_max = read_traces_to_memory(s)
 
         # initialize
-        x_plane_canvas = PlotWidget(s.xline, s.xlines, "x-lines", x_axis_indexes=('i-lines', s.ilines),
-                                    show_v_indicator=True, v_min_max=min_max)
-        i_plane_canvas = PlotWidget(s.iline, s.ilines, "i-lines", x_axis_indexes=('x-lines', s.xlines),
-                                    show_v_indicator=True, v_min_max=min_max)
-        depth_plane_canvas = PlotWidget(depth_planes, range(s.samples), "depth", x_axis_indexes=('i-lines', s.ilines),
-                                        y_axis_indexes=('x-lines', s.xlines),
-                                        show_v_indicator=True, show_h_indicator=True, v_min_max=min_max)
+        x_slice_widget = SliceWidget(s.xline, s.xlines,
+                                     x_axis_indexes=('i-lines', s.ilines.tolist()),
+                                     y_axis_indexes=('depth', range(s.samples)),
+                                     show_v_indicator=True,
+                                     v_min_max=min_max)
+        i_slice_widget = SliceWidget(s.iline, s.ilines,
+                                     x_axis_indexes=('x-lines', s.xlines.tolist()),
+                                     y_axis_indexes=('depth', range(s.samples)),
+                                     show_v_indicator=True,
+                                     v_min_max=min_max)
+        depth_slice_widget = SliceWidget(depth_slices, range(s.samples),
+                                         x_axis_indexes=('i-lines', s.ilines.tolist()),
+                                         y_axis_indexes=('x-lines', s.xlines.tolist()),
+                                         show_v_indicator=True,
+                                         show_h_indicator=True,
+                                         v_min_max=min_max)
 
         # attach signals
-        x_plane_canvas.plotCanvas.indexChanged.connect(line_monitor.ilineUpdated)
-        i_plane_canvas.plotCanvas.indexChanged.connect(line_monitor.xlineUpdated)
+        x_slice_widget.indexChanged.connect(line_monitor.ilineUpdated)
+        i_slice_widget.indexChanged.connect(line_monitor.xlineUpdated)
 
-        line_monitor.ilineChanged.connect(x_plane_canvas.set_vertical_line_indicator)
-        line_monitor.ilineChanged.connect(depth_plane_canvas.set_vertical_line_indicator)
-        line_monitor.ilineChanged.connect(i_plane_canvas.plotCanvas.update_image)
+        line_monitor.ilineChanged.connect(x_slice_widget.set_vertical_line_indicator)
+        line_monitor.ilineChanged.connect(depth_slice_widget.set_vertical_line_indicator)
+        line_monitor.ilineChanged.connect(i_slice_widget.update_image)
 
-        line_monitor.xlineChanged.connect(i_plane_canvas.set_vertical_line_indicator)
-        line_monitor.xlineChanged.connect(depth_plane_canvas.set_horizontal_line_indicator)
-        line_monitor.xlineChanged.connect(x_plane_canvas.plotCanvas.update_image)
+        line_monitor.xlineChanged.connect(i_slice_widget.set_vertical_line_indicator)
+        line_monitor.xlineChanged.connect(depth_slice_widget.set_horizontal_line_indicator)
+        line_monitor.xlineChanged.connect(x_slice_widget.update_image)
 
-        line_monitor.depthChanged.connect(depth_plane_canvas.plotCanvas.update_image)
+        line_monitor.depthChanged.connect(depth_slice_widget.update_image)
 
         # colormap signals
-        colormap_monitor.cmap_changed.connect(x_plane_canvas.set_cmap)
-        colormap_monitor.cmap_changed.connect(i_plane_canvas.set_cmap)
-        colormap_monitor.cmap_changed.connect(depth_plane_canvas.set_cmap)
+        colormap_monitor.cmap_changed.connect(x_slice_widget.set_cmap)
+        colormap_monitor.cmap_changed.connect(i_slice_widget.set_cmap)
+        colormap_monitor.cmap_changed.connect(depth_slice_widget.set_cmap)
 
         # layout
-        xdock = QtGui.QDockWidget("x-lines")
+        xdock = QtGui.QDockWidget("x-line")
         xdock.setFeatures(QtGui.QDockWidget.DockWidgetFloatable | QtGui.QDockWidget.DockWidgetMovable)
-        xdock.setWidget(x_plane_canvas)
+        xdock.setWidget(x_slice_widget)
 
-        idock = QtGui.QDockWidget("i-lines")
+        idock = QtGui.QDockWidget("i-line")
         idock.setFeatures(QtGui.QDockWidget.DockWidgetFloatable | QtGui.QDockWidget.DockWidgetMovable)
-        idock.setWidget(i_plane_canvas)
+        idock.setWidget(i_slice_widget)
 
-        ddock = QtGui.QDockWidget("depth plane")
+        ddock = QtGui.QDockWidget("depth slice")
         ddock.setFeatures(QtGui.QDockWidget.DockWidgetFloatable | QtGui.QDockWidget.DockWidgetMovable)
-        ddock.setWidget(depth_plane_canvas)
+        ddock.setWidget(depth_slice_widget)
 
         self.setDockOptions(QtGui.QMainWindow.AllowNestedDocks)
         self.addDockWidget(QtCore.Qt.TopDockWidgetArea, xdock)
         self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, idock)
         self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, ddock)
 
-        self.main_widget.setFocus()
-        self.setCentralWidget(self.main_widget)
-        self.main_widget.hide()
+        main_widget.setFocus()
+        self.setCentralWidget(main_widget)
+        main_widget.hide()
 
 
 def read_traces_to_memory(segy):
-    ''' read all samples into memory and identify min and max'''
+    ''' read all samples into memory and identify min and max. A temporary utility method to handle the
+    challenge of navigating  up and down in depth slices. As each depth slice consist of samples from all traces in
+    the file '''
     all_traces = np.empty(shape=((len(segy.ilines) * len(segy.xlines)), segy.samples), dtype=np.float32)
 
     min_value = sys.float_info.max
@@ -405,17 +172,15 @@ def read_traces_to_memory(segy):
     return transposed_traces, (min_value, max_value)
 
 
-
-
 def main():
     if len(sys.argv) < 2:
-        sys.exit("Usage: segyviewer.py [file]")
+        sys.exit("Usage: view.py [file]")
 
     filename = sys.argv[1]
 
     with segyio.open(filename, "r") as s:
         qApp = QtGui.QApplication(sys.argv)
-        aw = AppWindow(s)
+        aw = SegyViewer(s)
         aw.show()
         sys.exit(qApp.exec_())
 
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 9eca648..ee72a57 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,5 +1,5 @@
 configure_file(../tests/test-data/small.sgy test-data/small.sgy COPYONLY)
-configure_file(segyviewer.py segyviewer.py COPYONLY)
+
 
 add_python_example(python.examples.about about.py test-data/small.sgy INLINE_3D CROSSLINE_3D)
 add_python_example(python.examples.write write.py test-data/small.sgy)
diff --git a/examples/segyviewer.py b/examples/segyviewer.py
deleted file mode 100644
index 679e819..0000000
--- a/examples/segyviewer.py
+++ /dev/null
@@ -1,387 +0,0 @@
-import segyio
-
-from pylab import *
-
-from PyQt4 import QtGui, QtCore
-
-from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
-from matplotlib.figure import Figure
-import matplotlib.patches as patches
-
-
-class LineSelectionMonitor(QtCore.QObject):
-    ilineChanged = QtCore.pyqtSignal(int)
-    xlineChanged = QtCore.pyqtSignal(int)
-    depthChanged = QtCore.pyqtSignal(int)
-
-    def __init__(self, parent):
-        QtCore.QObject.__init__(self, parent)
-
-    def ilineUpdated(self, new_index):
-        self.ilineChanged.emit(new_index)
-
-    def xlineUpdated(self, new_index):
-        self.xlineChanged.emit(new_index)
-
-    def depthUpdated(self, new_index):
-        self.depthChanged.emit(new_index)
-
-class ColorMapMonitor(QtCore.QObject):
-    cmap_changed = QtCore.pyqtSignal(str)
-
-    def __init__(self, parent=None):
-        QtCore.QObject.__init__(self, parent)
-
-    def colormapUpdated(self, value):
-        self.cmap_changed.emit(str(value))
-
-
-class PlaneCanvas(FigureCanvas):
-    """
-    Generic plot canvas for all plane views
-    """
-    indexChanged = QtCore.pyqtSignal(int)
-
-    def __init__(self, planes, indexes, axis_indexes, dataset_title, cmap, display_horizontal_indicator=False,
-                 display_vertical_indicator=False, parent=None, width=800, height=100, dpi=20, v_min_max=None):
-
-        self.planes = planes
-        self.indexes = indexes
-        self.x_axis_indexes, self.y_axis_indexes = axis_indexes[0], axis_indexes[1]
-
-        self.dataset_title = dataset_title
-
-        self.fig = Figure(figsize=(width, height), dpi=dpi, facecolor='white')
-        FigureCanvas.__init__(self, self.fig)
-
-        self.setParent(parent)
-
-        self.axes = self.fig.add_subplot(111)
-
-        self.axes.tick_params(labelsize=30)
-
-        # the default colormap
-        self.cmap = cmap
-
-        self.im = self.axes.imshow(planes[indexes[0]].T, interpolation="nearest", aspect="auto", cmap=self.cmap, vmin=v_min_max[0], vmax=v_min_max[1])
-
-        self.current_index = 0
-
-        # connecting matplotlib mouse signals
-        self.mpl_connect('motion_notify_event', self.mouse_moved)
-        self.mpl_connect('button_press_event', self.mouse_clicked)
-        self.mpl_connect('axes_leave_event', self.mouse_left)
-
-        if display_vertical_indicator:
-            self.verdical_indicator_rect = self.axes.add_patch(
-                patches.Rectangle(
-                    (-0.5, 0),  # (x,y)
-                    1,  # width - bredde er pr dot ikke pixel...
-                    len(self.planes[self.indexes[0]][0]),  # height / depth
-                    fill=False,
-                    alpha=1,
-                    color='black',
-                    linestyle='--',
-                    linewidth=2
-                )
-            )
-
-        if display_horizontal_indicator:
-            self.horizontal_indicator_rect = self.axes.add_patch(
-                patches.Rectangle(
-                    (-0.5, 0),
-                    len(self.planes[self.indexes[0]][0]),
-                    1,
-                    fill=False,
-                    alpha=1,
-                    color='black',
-                    linestyle='--',
-                    linewidth=2
-                )
-            )
-
-        self.disabled_overlay = self.axes.add_patch(
-            patches.Rectangle(
-                (-0.5, 0),  # (x,y)
-                len(self.planes[self.indexes[0]][0]),
-                len(self.planes[self.indexes[0]][0]),
-                alpha=0.5,
-                color='gray',
-                visible=False
-            )
-        )
-
-
-    def mouse_left(self, evt):
-        # for now do nothing
-        # self.set_vertical_line_indicator(self.current_index)
-        pass
-
-
-    def mouse_clicked(self, evt):
-        if evt.inaxes:
-            self.current_index = int(evt.xdata)
-            self.indexChanged.emit(self.x_axis_indexes[int(evt.xdata)])
-
-    def mouse_moved(self, evt):
-
-        # if evt.inaxes:
-        #   self.set_vertical_line_indicator(int(evt.xdata))
-        pass
-
-    def set_colormap(self, cmap):
-        self.cmap = cmap
-        self.im.set_cmap(cmap)
-        self.draw()
-
-    def update_image(self, index):
-        self.im.set_data(self.planes[index].T)
-        self.draw()
-
-    def set_vertical_line_indicator(self, line_index):
-        self.verdical_indicator_rect.set_x(self.x_axis_indexes.index(line_index) - 0.5)
-        self.verdical_indicator_rect.set_y(0)
-        self.draw()
-
-    def set_horizontal_line_indicator(self, line_index):
-        self.horizontal_indicator_rect.set_x(-0.5)
-
-        self.horizontal_indicator_rect.set_y(self.y_axis_indexes.index(line_index))
-        self.draw()
-
-    def enable_overlay(self):
-        self.disabled_overlay.set_visible(True)
-        self.draw()
-
-    def disable_overlay(self):
-        self.disabled_overlay.set_visible(False)
-        self.draw()
-
-
-class PlotWidget(QtGui.QWidget):
-    """
-    Main widget holding the figure and slider
-    """
-
-    def __init__(self, planes, indexes, axis_indexes, dataset_title, default_cmap='seismic',
-                 show_h_indicator=False, show_v_indicator=False, v_min_max=None):
-        super(PlotWidget, self).__init__()
-
-        self.planes = planes
-        self.indexes = indexes
-        self.dataset_title = dataset_title
-        self.default_cmap = default_cmap
-        self.show_h_indicator = show_h_indicator
-        self.show_v_indicator = show_v_indicator
-        self.setAutoFillBackground(True)
-        p = self.palette()
-        p.setColor(self.backgroundRole(), QtCore.Qt.white)
-        self.setPalette(p)
-
-
-        self.plotCanvas = PlaneCanvas(self.planes, self.indexes, axis_indexes, self.dataset_title, self.default_cmap,
-                                      display_horizontal_indicator=self.show_h_indicator,
-                                      display_vertical_indicator=self.show_v_indicator, v_min_max=v_min_max)
-
-        self.layout = QtGui.QVBoxLayout(self)
-        self.layout.addWidget(self.plotCanvas)
-
-    def set_cmap(self, action):
-        self.plotCanvas.set_colormap(str(action))
-
-    def set_vertical_line_indicator(self, line):
-        self.plotCanvas.set_vertical_line_indicator(line)
-
-    def set_horizontal_line_indicator(self, line):
-        self.plotCanvas.set_horizontal_line_indicator(line)
-
-
-class TopMenu(QtGui.QMenuBar):
-    def __init__(self, parent, colormap_monitor):
-        super(QtGui.QMenuBar, self).__init__(parent)
-
-        self.fileMenu = self.addMenu('&File')
-        exitAction = QtGui.QAction('&Exit', self)
-        self.fileMenu.addAction(exitAction)
-
-        self.viewMenu = self.addMenu('&View')
-        self.colormapMenu = self.viewMenu.addMenu("&Colormap")
-
-        self.colormap_monitor = colormap_monitor
-
-        self.colormap_monitor.cmap_changed.connect(self.set_selected_cmap)
-
-        def colormapChanger(color_map_name):
-            def performColorMapChange():
-                self.colormap_monitor.colormapUpdated(color_map_name)
-            return performColorMapChange
-
-        for item in ['seismic', 'spectral', 'RdGy', 'hot', 'jet', 'gray']:
-            action = self.colormapMenu.addAction(item)
-            action.setCheckable(True)
-            action.triggered.connect(colormapChanger(item))
-
-    def set_selected_cmap(self, cmap_name):
-        for item in self.colormapMenu.actions():
-            item.setChecked(str(item.text()) == cmap_name)
-
-
-class LineSelector(QtGui.QWidget):
-    indexChanged = QtCore.pyqtSignal(int)
-
-    def __init__(self, parent, label, indexes, monitor_func):
-        super(QtGui.QWidget, self).__init__(parent)
-        self.label = label
-        self.indexes = indexes
-        self.monitor_func = monitor_func
-
-        self.layout = QtGui.QHBoxLayout()
-        self.slabel = QtGui.QLabel(self.label)
-        self.sbox = QtGui.QSpinBox(self)
-        self.sbox.setRange(self.indexes[0], self.indexes[-1])
-        self.sbox.valueChanged.connect(self.monitor_func)
-        self.layout.addWidget(self.slabel)
-        self.layout.addWidget(self.sbox)
-        self.setLayout(self.layout)
-
-    def index_changed(self, val):
-        self.indexChanged.emit(val)
-
-    def set_index(self, val):
-        self.sbox.blockSignals(True)
-        self.sbox.setValue(val)
-        self.sbox.blockSignals(False)
-
-
-class ToolBar(QtGui.QToolBar):
-    def __init__(self, xline_indexes, iline_indexes, depth_indexes, line_selection_monitor):
-        super(ToolBar, self).__init__("")
-        self.xline_indexes = xline_indexes
-        self.iline_indexes = iline_indexes
-        self.depth_indexes = depth_indexes
-        self.line_selection_monitor = line_selection_monitor
-
-        # xline
-        self.xline_selector = LineSelector(self, "xline", self.xline_indexes, self.line_selection_monitor.xlineUpdated)
-        self.line_selection_monitor.xlineChanged.connect(self.xline_selector.set_index)
-        self.addWidget(self.xline_selector)
-
-        # iline
-        self.iline_selector = LineSelector(self, "iline", self.iline_indexes, self.line_selection_monitor.ilineUpdated)
-        self.line_selection_monitor.ilineChanged.connect(self.iline_selector.set_index)
-        self.addWidget(self.iline_selector)
-
-        # iline
-        self.depth_selector = LineSelector(self, "depth", self.depth_indexes, self.line_selection_monitor.depthUpdated)
-        self.addWidget(self.depth_selector)
-
-
-class AppWindow(QtGui.QMainWindow):
-    def __init__(self, s):
-        QtGui.QMainWindow.__init__(self)
-        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
-        self.setWindowTitle("Segy Viewer")
-        self.main_widget = QtGui.QWidget(self)
-
-        # signal monitors
-        colormap_monitor = ColorMapMonitor(self)
-        line_monitor = LineSelectionMonitor(self)
-
-        self.setMenuBar(TopMenu(self, colormap_monitor))
-        self.addToolBar(ToolBar(s.xlines, s.ilines, range(s.samples), line_monitor))
-        self.statusBar()
-
-        depth_planes, min_max = read_traces_to_memory(s)
-
-        # initialize
-        x_plane_canvas = PlotWidget(s.xline, s.xlines, (s.ilines, 0), "xlines", show_v_indicator=True, v_min_max=min_max)
-        i_plane_canvas = PlotWidget(s.iline, s.ilines, (s.xlines, 0), "ilines", show_v_indicator=True, v_min_max=min_max)
-        depth_plane_canvas = PlotWidget(depth_planes, range(s.samples), (s.xlines, s.ilines), "depth",
-                                        show_v_indicator=True, show_h_indicator=True, v_min_max=min_max)
-
-        # attach signals
-        x_plane_canvas.plotCanvas.indexChanged.connect(line_monitor.ilineUpdated)
-        i_plane_canvas.plotCanvas.indexChanged.connect(line_monitor.xlineUpdated)
-
-        line_monitor.ilineChanged.connect(x_plane_canvas.set_vertical_line_indicator)
-        line_monitor.ilineChanged.connect(depth_plane_canvas.set_horizontal_line_indicator)
-        line_monitor.ilineChanged.connect(i_plane_canvas.plotCanvas.update_image)
-
-        line_monitor.xlineChanged.connect(i_plane_canvas.set_vertical_line_indicator)
-        line_monitor.xlineChanged.connect(depth_plane_canvas.set_vertical_line_indicator)
-        line_monitor.xlineChanged.connect(x_plane_canvas.plotCanvas.update_image)
-
-        line_monitor.depthChanged.connect(depth_plane_canvas.plotCanvas.update_image)
-
-        # colormap signals
-        colormap_monitor.cmap_changed.connect(x_plane_canvas.set_cmap)
-        colormap_monitor.cmap_changed.connect(i_plane_canvas.set_cmap)
-        colormap_monitor.cmap_changed.connect(depth_plane_canvas.set_cmap)
-
-        # layout
-        xdock = QtGui.QDockWidget("x-lines")
-        xdock.setFeatures(QtGui.QDockWidget.DockWidgetFloatable | QtGui.QDockWidget.DockWidgetMovable)
-        xdock.setWidget(x_plane_canvas)
-
-        idock = QtGui.QDockWidget("i-lines")
-        idock.setFeatures(QtGui.QDockWidget.DockWidgetFloatable | QtGui.QDockWidget.DockWidgetMovable)
-        idock.setWidget(i_plane_canvas)
-
-        ddock = QtGui.QDockWidget("depth plane")
-        ddock.setFeatures(QtGui.QDockWidget.DockWidgetFloatable | QtGui.QDockWidget.DockWidgetMovable)
-        ddock.setWidget(depth_plane_canvas)
-
-        self.setDockOptions(QtGui.QMainWindow.AllowNestedDocks)
-        self.addDockWidget(QtCore.Qt.TopDockWidgetArea, xdock)
-        self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, idock)
-        self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, ddock)
-
-        self.main_widget.setFocus()
-        self.setCentralWidget(self.main_widget)
-        self.main_widget.hide()
-
-
-def read_traces_to_memory(segy):
-    ''' read all samples into memory and identify min and max'''
-
-    all_traces = np.empty(shape=((len(segy.ilines) * len(segy.xlines)), segy.samples), dtype=np.float32)
-
-    min_value = sys.float_info.max
-    max_value = sys.float_info.min
-
-    for i, t in enumerate(segy.trace):
-        all_traces[i] = t
-
-        local_min = np.nanmin(t)
-        local_max = np.nanmax(t)
-
-        if np.isfinite(local_min):
-            min_value = min(local_min, min_value)
-
-        if np.isfinite(local_max):
-            max_value = max(local_max, max_value)
-
-    all_traces2 = all_traces.reshape(len(segy.ilines), len(segy.xlines), segy.samples)
-
-    transposed_traces = all_traces2.transpose(2, 0, 1)
-
-    return transposed_traces, (min_value,max_value)
-
-
-
-
-def main():
-    if len(sys.argv) < 2:
-        sys.exit("Usage: segyviewer.py [file]")
-
-    filename = sys.argv[1]
-
-    with segyio.open(filename, "r") as s:
-        qApp = QtGui.QApplication(sys.argv)
-        aw = AppWindow(s)
-        aw.show()
-        sys.exit(qApp.exec_())
-
-
-if __name__ == '__main__':
-    main()
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index bfaa4bb..d857a01 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -30,4 +30,5 @@ if (PYTHONLIBS_FOUND)
             $<TARGET_FILE:_segyio> ${CMAKE_BINARY_DIR}/python/segyio/_segyio.so)
 endif ()
 
-add_subdirectory(segyio)
\ No newline at end of file
+add_subdirectory(segyio)
+add_subdirectory(segyview)
diff --git a/python/segyview/CMakeLists.txt b/python/segyview/CMakeLists.txt
new file mode 100644
index 0000000..2b157d1
--- /dev/null
+++ b/python/segyview/CMakeLists.txt
@@ -0,0 +1,8 @@
+set(PYTHON_SOURCES
+    __init__.py
+    linenavigationbar.py
+    segyplot.py
+    slicewidget.py
+    )
+
+add_python_package(segyview segyview "${PYTHON_SOURCES}")
diff --git a/python/segyview/__init__.py b/python/segyview/__init__.py
new file mode 100644
index 0000000..640d109
--- /dev/null
+++ b/python/segyview/__init__.py
@@ -0,0 +1,10 @@
+from .segyplot import SegyPlot
+
+try:
+    from .linenavigationbar import LineNavigationBar
+    from .slicewidget import SliceWidget
+except ImportError as e:
+    import sys
+    import traceback
+    exc_type, exc_value, exc_traceback = sys.exc_info()
+    traceback.print_exception(exc_type, exc_value, exc_traceback, limit=2, file=sys.stderr)
diff --git a/python/segyview/linenavigationbar.py b/python/segyview/linenavigationbar.py
new file mode 100644
index 0000000..76cfb6c
--- /dev/null
+++ b/python/segyview/linenavigationbar.py
@@ -0,0 +1,50 @@
+from PyQt4 import QtGui, QtCore
+
+
+class LineSelector(QtGui.QWidget):
+    indexChanged = QtCore.pyqtSignal(int)
+
+    def __init__(self, parent, label, indexes, monitor_func):
+        super(QtGui.QWidget, self).__init__(parent)
+        self.label = label
+        self.indexes = indexes
+        self.monitor_func = monitor_func
+
+        self.layout = QtGui.QHBoxLayout()
+        self.slabel = QtGui.QLabel(self.label)
+        self.sbox = QtGui.QSpinBox(self)
+        self.sbox.setRange(self.indexes[0], self.indexes[-1])
+        self.sbox.valueChanged.connect(self.monitor_func)
+        self.layout.addWidget(self.slabel)
+        self.layout.addWidget(self.sbox)
+        self.setLayout(self.layout)
+
+    def index_changed(self, val):
+        self.indexChanged.emit(val)
+
+    def set_index(self, val):
+        self.sbox.blockSignals(True)
+        self.sbox.setValue(val)
+        self.sbox.blockSignals(False)
+
+class LineNavigationBar(QtGui.QToolBar):
+    def __init__(self, xline_indexes, iline_indexes, depth_indexes, line_selection_monitor):
+        super(LineNavigationBar, self).__init__("")
+        self.xline_indexes = xline_indexes
+        self.iline_indexes = iline_indexes
+        self.depth_indexes = depth_indexes
+        self.line_selection_monitor = line_selection_monitor
+
+        # xline
+        self.xline_selector = LineSelector(self, "x-line", self.xline_indexes, self.line_selection_monitor.xlineUpdated)
+        self.line_selection_monitor.xlineChanged.connect(self.xline_selector.set_index)
+        self.addWidget(self.xline_selector)
+
+        # iline
+        self.iline_selector = LineSelector(self, "i-line", self.iline_indexes, self.line_selection_monitor.ilineUpdated)
+        self.line_selection_monitor.ilineChanged.connect(self.iline_selector.set_index)
+        self.addWidget(self.iline_selector)
+
+        # depth
+        self.depth_selector = LineSelector(self, "depth", self.depth_indexes, self.line_selection_monitor.depthUpdated)
+        self.addWidget(self.depth_selector)
diff --git a/python/segyview/segyplot.py b/python/segyview/segyplot.py
new file mode 100644
index 0000000..d579d86
--- /dev/null
+++ b/python/segyview/segyplot.py
@@ -0,0 +1,117 @@
+from matplotlib.ticker import FuncFormatter, MaxNLocator
+import matplotlib.patches as patches
+
+
+class SegyPlot(object):
+
+    """
+    SegyPlot plots a segy slice and line indicators on the provided axes.
+    """
+
+    def __init__(self, slices, indexes, axes, cmap='seismic', x_axis_indexes=None, y_axis_indexes=None,
+                 display_horizontal_indicator=False,
+                 display_vertical_indicator=False, v_min_max=None):
+
+        self.slices = slices
+        self.indexes = indexes
+        self.cmap = cmap
+
+        self.vmin, self.vmax = v_min_max or (None, None)
+
+        self.plane_height = len(self.slices[self.indexes[0]][0])
+        self.plane_width = len(self.slices[self.indexes[0]][:])
+
+        self.x_axis_name, self.x_axis_indexes = x_axis_indexes or (None, None)
+        self.y_axis_name, self.y_axis_indexes = y_axis_indexes or (None, None)
+
+        self.slice_axes = axes
+        self.slice_axes.tick_params(axis='both', labelsize=30)
+
+        if self.x_axis_indexes is not None:
+            def x_axis_label_formatter(val, position):
+                if 0 <= val < len(self.x_axis_indexes):
+                    return self.x_axis_indexes[int(val)]
+                return ''
+
+            self.slice_axes.set_xlabel(self.x_axis_name, fontsize=40)
+            self.slice_axes.get_xaxis().set_major_formatter(FuncFormatter(x_axis_label_formatter))
+            self.slice_axes.get_xaxis().set_major_locator(MaxNLocator(20))  # max 20 ticks are shown
+
+        if self.y_axis_indexes is not None:
+            def y_axis_label_formatter(val, position):
+                if 0 <= val < len(self.y_axis_indexes):
+                    return self.y_axis_indexes[int(val)]
+                return ''
+
+            self.slice_axes.set_ylabel(self.y_axis_name, fontsize=40)
+            self.slice_axes.get_yaxis().set_major_formatter(FuncFormatter(y_axis_label_formatter))
+            self.slice_axes.get_yaxis().set_major_locator(MaxNLocator(10))  # max 20 ticks are shown
+
+        self.im = self.slice_axes.imshow(slices[indexes[0]].T,
+                                         interpolation="nearest",
+                                         aspect="auto",
+                                         cmap=self.cmap,
+                                         vmin=self.vmin,
+                                         vmax=self.vmax)
+
+        if display_vertical_indicator:
+            self.vertical_indicator_rect = self.slice_axes.add_patch(
+                patches.Rectangle(
+                    (-0.5, -0.5),
+                    1,
+                    self.plane_height,
+                    fill=False,
+                    alpha=1,
+                    color='black',
+                    linestyle='--',
+                    linewidth=2
+                )
+            )
+
+        if display_horizontal_indicator:
+            self.horizontal_indicator_rect = self.slice_axes.add_patch(
+                patches.Rectangle(
+                    (-0.5, -0.5),
+                    self.plane_width,
+                    1,
+                    fill=False,
+                    alpha=1,
+                    color='black',
+                    linestyle='--',
+                    linewidth=2
+                )
+            )
+
+        self.disabled_overlay = self.slice_axes.add_patch(
+            patches.Rectangle(
+                (-0.5, -0.5),  # (x,y)
+                len(self.slices[self.indexes[0]][0]),
+                len(self.slices[self.indexes[0]][0]),
+                alpha=0.5,
+                color='gray',
+                visible=False
+            )
+        )
+
+    def set_colormap(self, cmap):
+        self.cmap = cmap
+        self.im.set_cmap(cmap)
+
+    def update_image(self, index):
+        self.im.set_data(self.slices[index].T)
+
+    def set_vertical_line_indicator(self, line_index):
+        if self.x_axis_indexes is not None:
+            self.vertical_indicator_rect.set_x(self.x_axis_indexes.index(line_index) - 0.5)
+
+    def set_horizontal_line_indicator(self, line_index):
+        if self.y_axis_indexes is not None:
+            line_index = self.y_axis_indexes.index(line_index)
+        self.horizontal_indicator_rect.set_y(line_index - 0.5)
+
+    def enable_overlay(self):
+        self.disabled_overlay.set_visible(True)
+
+    def disable_overlay(self):
+        self.disabled_overlay.set_visible(False)
+
diff --git a/python/segyview/slicewidget.py b/python/segyview/slicewidget.py
new file mode 100644
index 0000000..1d21e05
--- /dev/null
+++ b/python/segyview/slicewidget.py
@@ -0,0 +1,78 @@
+from PyQt4 import QtGui, QtCore
+
+from segyplot import SegyPlot
+from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
+from matplotlib.figure import  Figure
+
+
+class SliceWidget(QtGui.QWidget):
+    """
+    Main widget holding the slice matplotlib Figure wrapped in FigureCanvasQTAgg.
+    """
+    indexChanged = QtCore.pyqtSignal(int)
+
+    def __init__(self, slices, indexes, dataset_title=None, default_cmap='seismic',
+                 x_axis_indexes=None, y_axis_indexes=None,
+                 show_h_indicator=False, show_v_indicator=False, v_min_max=None):
+        super(SliceWidget, self).__init__()
+
+        self.slices = slices
+        self.indexes = indexes
+
+        self.x_axis_name, self.x_axis_indexes = x_axis_indexes or (None, None)
+        self.y_axis_name, self.y_axis_indexes = y_axis_indexes or (None, None)
+
+        self.default_cmap = default_cmap
+
+        self.show_h_indicator = show_h_indicator
+        self.show_v_indicator = show_v_indicator
+
+        self.palette().setColor(self.backgroundRole(), QtCore.Qt.white)
+
+        self.current_index = 0
+
+        # setting up the figure and canvas
+        self.figure = Figure(figsize=(800, 200), dpi=20, facecolor='white')
+
+        self.axes = self.figure.add_subplot(111)
+
+        self.segy_plot = SegyPlot(self.slices, self.indexes, self.axes, self.default_cmap,
+                                        x_axis_indexes=x_axis_indexes, y_axis_indexes=y_axis_indexes,
+                                        display_horizontal_indicator=self.show_h_indicator,
+                                        display_vertical_indicator=self.show_v_indicator, v_min_max=v_min_max)
+
+        self.figure_canvas = FigureCanvas(self.figure)
+        self.figure_canvas.setParent(self)
+
+        # connect to mouse click events
+        self.figure_canvas.mpl_connect('button_press_event', self._mouse_clicked)
+
+        # widget layout
+        self.layout = QtGui.QVBoxLayout(self)
+        self.layout.addWidget(self.figure_canvas)
+
+    def _mouse_clicked(self, evt):
+        if evt.inaxes is not None:
+            self.current_index = int(evt.xdata)
+            self._signal_index_change(self.current_index)
+
+    def _signal_index_change(self, x):
+        if self.x_axis_indexes is not None:
+            self.index_changed.emit(self.x_axis_indexes[x])
+
+
+    def update_image(self, index):
+        self.segy_plot.update_image(index)
+        self.figure_canvas.draw()
+
+    def set_cmap(self, action):
+        self.segy_plot.set_colormap(str(action))
+        self.figure_canvas.draw()
+
+    def set_vertical_line_indicator(self, line):
+        self.segy_plot.set_vertical_line_indicator(line)
+        self.figure_canvas.draw()
+
+    def set_horizontal_line_indicator(self, line):
+        self.segy_plot.set_horizontal_line_indicator(line)
+        self.figure_canvas.draw()

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/segyio.git



More information about the debian-science-commits mailing list