class SegyTabWidget(QWidget): def __init__(self, segywidgets=None, parent=None): QWidget.__init__(self, parent) """ :type segywidgets: list[segyviewlib.SegyViewWidget] :type parent: QObject """ self._context = None self._segywidgets = segywidgets if segywidgets else [] self._tab_widget = QTabWidget() self.initialize() def initialize(self): if len(self._segywidgets) == 0: return layout = QVBoxLayout() slice_data_source, slice_models = self._setup_model_source() self._context = SliceViewContext(slice_models, slice_data_source, has_data=False) self._context.show_indicators(True) self._context.data_changed.connect(self._data_changed) self._slice_view_widget = None # Required for settingswindow self._settings_window = SettingsWindow(self._context, self) self._settings_window.min_max_changed.connect(self._min_max_changed) self._settings_window.indicators_changed.connect( self._indicators_changed) self._settings_window.interpolation_changed.connect( self._interpolation_changed) self._settings_window.samples_unit_changed.connect( self._samples_unit_changed) self._modify_qtree(self._settings_window.qtree, [3, 5]) self._toolbar = self._create_toolbar() self._local_data_changed(self._segywidgets[0].context.models) for i, segywidget in enumerate(self._segywidgets): self.add_segy_view_widget(i, segywidget) self._tab_widget.currentChanged.connect(self._tab_changed) layout.addWidget(self._toolbar) layout.addWidget(self._tab_widget) self.setLayout(layout) @property def _current_ctxt(self): """ :rtype: SliceViewContext """ return self._tab_widget.currentWidget().context @property def _ctxs(self): """ :rtype: list of SliceViewContext """ return [ self._tab_widget.widget(index).context for index in range(0, self._tab_widget.count()) ] def count(self): """ :rtype: int """ return self._tab_widget.count() def add_segy_view_widget(self, ind, widget, name=None): """ :param widget: The SegyViewWidget that will be added to the SegyTabWidget :type widget: SegyViewWidget """ if self._context is None: self._segywidgets.append(widget) self.initialize() return 0 # return 0 for first widget index self._tab_widget.updatesEnabled = False widget.show_toolbar(toolbar=True, layout_combo=False, colormap=True, save=True, settings=True) self._modify_qtree(widget.settings_window.qtree, [0, 1, 2, 4]) if name is None: name = os.path.basename(widget.slice_data_source.source_filename) id = self._tab_widget.insertTab(ind, widget, name) widget.context.data_changed.connect(self._local_data_changed) self._tab_widget.updatesEnabled = True return id def remove_segy_view_widget(self, index): """ :param index: The index of the tab to be removed :type index: int """ self._tab_widget.removeTab(index) def _local_data_changed(self, models=None): for m in self._context.models: for current_model in models: if current_model.index_direction == m.index_direction: m.index = current_model.index if current_model.x_index_direction == m.index_direction: m.x_index = current_model.index if current_model.y_index_direction == m.index_direction: m.y_index = current_model.index m.dirty = True self._update_models() self._context.context_changed.emit() def _data_changed(self): self._update_models() self._update_views() def _min_max_changed(self, values, axis): min, max = values for ctxt in self._ctxs: with blocked_update(ctxt, self._current_ctxt): if axis == 'depth': ctxt.set_y_view_limits(SD.crossline, min, max) ctxt.set_y_view_limits(SD.inline, min, max) elif axis == 'iline': ctxt.set_x_view_limits(SD.crossline, min, max) ctxt.set_x_view_limits(SD.depth, min, max) elif axis == 'xline': ctxt.set_x_view_limits(SD.inline, min, max) ctxt.set_y_view_limits(SD.depth, min, max) self._update_views() def _interpolation_changed(self, interpolation_name): for ctxt in self._ctxs: with blocked_update(ctxt, self._current_ctxt): ctxt.set_interpolation(interpolation_name) def _indicators_changed(self, visible): for ctxt in self._ctxs: with blocked_update(ctxt, self._current_ctxt): ctxt.show_indicators(visible) def _samples_unit_changed(self, val): for ctxt in self._ctxs: with blocked_update(ctxt, self._current_ctxt): ctxt.samples_unit = val def _update_models(self): dirty_models = [m for m in self._context.models if m.dirty] local_models = list( chain.from_iterable([ctx.models for ctx in self._ctxs])) for m in dirty_models: for local_m in local_models: if local_m.index_direction == m.index_direction: local_m.index = m.index if local_m.x_index_direction == m.index_direction: local_m.x_index = m.index if local_m.y_index_direction == m.index_direction: local_m.y_index = m.index local_m.dirty = True m.dirty = False def _update_views(self): self._tab_widget.currentWidget().context.context_changed.emit() self._tab_widget.currentWidget().context.load_data() def _tab_changed(self): if self._tab_widget.count() == 0: return widget_spec = self._tab_widget.currentWidget( ).slice_view_widget.current_layout() setting_spec = self.layout_combo.get_current_layout() if widget_spec != setting_spec: self._tab_widget.currentWidget().slice_view_widget.set_plot_layout( setting_spec) self._update_views() def _setup_model_source(self): slice_data_source = SliceDataSource(False) directions = [SD.inline, SD.crossline, SD.depth] source_template = self._segywidgets[0].slice_data_source for direction in directions: slice_data_source.set_indexes( direction, source_template.indexes_for_direction(direction)) inline = SliceModel("Inline", SD.inline, SD.crossline, SD.depth) xline = SliceModel("Crossline", SD.crossline, SD.inline, SD.depth) depth = SliceModel("Depth", SD.depth, SD.inline, SD.crossline) slice_models = [inline, xline, depth] return slice_data_source, slice_models def __del__(self): self.layout_combo.layout_changed.disconnect(self._plot_layout_changed) def _create_toolbar(self): toolbar = QToolBar() toolbar.setFloatable(False) toolbar.setMovable(False) self.layout_combo = LayoutCombo() toolbar.addWidget(self.layout_combo) self.layout_combo.layout_changed.connect(self._plot_layout_changed) self._settings_button = QToolButton() self._settings_button.setToolTip("Toggle settings visibility") self._settings_button.setIcon(resource_icon("cog.png")) self._settings_button.setCheckable(True) self._settings_button.toggled.connect(self._show_settings) toolbar.addWidget(self._settings_button) def toggle_on_close(event): self._settings_button.setChecked(False) event.accept() self._settings_window.closeEvent = toggle_on_close return toolbar def _modify_qtree(self, tree, items): for i in items: tree.setRowHidden(i, QModelIndex(), True) def _plot_layout_changed(self, spec): self._tab_widget.currentWidget().slice_view_widget.set_plot_layout( spec) def _show_settings(self, toggled): self._settings_window.setVisible(toggled) if self._settings_window.isMinimized(): self._settings_window.showNormal()
class SegyViewWidget(QWidget): def __init__(self, filename, show_toolbar=True, color_maps=None, width=11.7, height=8.3, dpi=100, segyioargs = {}, parent=None): QWidget.__init__(self, parent) inline = SliceModel("Inline", SD.inline, SD.crossline, SD.depth) xline = SliceModel("Crossline", SD.crossline, SD.inline, SD.depth) depth = SliceModel("Depth", SD.depth, SD.inline, SD.crossline) slice_models = [inline, xline, depth] slice_data_source = SliceDataSource(filename, **segyioargs) self._slice_data_source = slice_data_source self._context = SliceViewContext(slice_models, slice_data_source) self._context.show_indicators(True) self._slice_view_widget = SliceViewWidget(self._context, width, height, dpi, self) layout = QVBoxLayout() self._settings_window = SettingsWindow(self._context, self) self._toolbar = self._create_toolbar(color_maps) self._toolbar.setVisible(show_toolbar) layout.addWidget(self._toolbar) layout.addWidget(self._slice_view_widget) self.setLayout(layout) def toolbar(self): """ :rtype: QToolBar """ return self._toolbar def _create_toolbar(self, color_maps): toolbar = QToolBar() toolbar.setFloatable(False) toolbar.setMovable(False) layout_combo = LayoutCombo() toolbar.addWidget(layout_combo) layout_combo.layout_changed.connect(self._slice_view_widget.set_plot_layout) # self._colormap_combo = ColormapCombo(['seismic', 'spectral', 'RdGy', 'hot', 'jet', 'gray']) self._colormap_combo = ColormapCombo(color_maps) self._colormap_combo.currentIndexChanged[int].connect(self._colormap_changed) toolbar.addWidget(self._colormap_combo) save_button = QToolButton() save_button.setToolTip("Save as image") save_button.setIcon(resource_icon("table_export.png")) save_button.clicked.connect(self._save_figure) toolbar.addWidget(save_button) self._settings_button = QToolButton() self._settings_button.setToolTip("Toggle settings visibility") self._settings_button.setIcon(resource_icon("cog.png")) self._settings_button.setCheckable(True) self._settings_button.toggled.connect(self._show_settings) toolbar.addWidget(self._settings_button) def toggle_on_close(event): self._settings_button.setChecked(False) event.accept() self._settings_window.closeEvent = toggle_on_close self._colormap_combo.setCurrentIndex(45) layout_combo.setCurrentIndex(4) return toolbar def _colormap_changed(self, index): colormap = str(self._colormap_combo.itemText(index)) self._context.set_colormap(colormap) def _interpolation_changed(self, index): interpolation_name = str(self._interpolation_combo.itemText(index)) self._context.set_interpolation(interpolation_name) def _save_figure(self): formats = "Portable Network Graphic (*.png);;Adobe Acrobat (*.pdf);;Scalable Vector Graphics (*.svg)" output_file = QFileDialog.getSaveFileName(self, "Save as image", "untitled.png", formats) output_file = str(output_file).strip() if len(output_file) == 0: return image_size = self._context.image_size if not image_size: fig = self._slice_view_widget else: w, h, dpi = image_size fig = SliceViewWidget(self._context, width = w, height = h, dpi = dpi) fig.set_plot_layout(self._slice_view_widget.layout_figure().current_layout()) fig.layout_figure().savefig(output_file) def set_source_filename(self, filename): self._slice_data_source.set_source_filename(filename) def as_depth(self): self._context.samples_unit = 'Depth (m)' def _show_settings(self, toggled): self._settings_window.setVisible(toggled) if self._settings_window.isMinimized(): self._settings_window.showNormal()
class SegyViewWidget(QWidget): def __init__(self, filename, show_toolbar=True, color_maps=None, width=11.7, height=8.3, dpi=100, segyioargs={}, parent=None): QWidget.__init__(self, parent) inline = SliceModel("Inline", SD.inline, SD.crossline, SD.depth) xline = SliceModel("Crossline", SD.crossline, SD.inline, SD.depth) depth = SliceModel("Depth", SD.depth, SD.inline, SD.crossline) slice_models = [inline, xline, depth] slice_data_source = SliceDataSource(filename, **segyioargs) self._slice_data_source = slice_data_source self._context = SliceViewContext(slice_models, slice_data_source) self._context.show_indicators(True) self._slice_view_widget = SliceViewWidget(self._context, width, height, dpi, self) layout = QVBoxLayout() self._settings_window = SettingsWindow(self._context, self) self._help_window = HelpWindow(self) self._toolbar = self._create_toolbar(color_maps) self._toolbar.setVisible(show_toolbar) layout.addWidget(self._toolbar) layout.addWidget(self._slice_view_widget) self.setLayout(layout) @property def context(self): """ :rtype: SliceViewContext""" return self._context @property def slice_data_source(self): """ :rtype: SliceDataSource""" return self._slice_data_source @property def toolbar(self): """ :rtype: QToolBar """ return self._toolbar @property def slice_view_widget(self): """ :rtype: SliceViewWidget """ return self._slice_view_widget @property def settings_window(self): """ :rtype: QWidget """ return self._settings_window @property def help_window(self): """ :rtype: QWidget """ return self._help_window # custom signal slots are required to be manually disconnected # https://stackoverflow.com/questions/15600014/pyqt-disconnect-slots-new-style def __del__(self): self._layout_combo.layout_changed.disconnect( self._slice_view_widget.set_plot_layout) def _create_toolbar(self, color_maps): toolbar = QToolBar() toolbar.setFloatable(False) toolbar.setMovable(False) self._layout_combo = LayoutCombo() self._layout_combo_action = QWidgetAction(self._layout_combo) self._layout_combo_action.setDefaultWidget(self._layout_combo) toolbar.addAction(self._layout_combo_action) self._layout_combo.layout_changed.connect( self._slice_view_widget.set_plot_layout) # self._colormap_combo = ColormapCombo(['seismic', 'spectral', 'RdGy', 'hot', 'jet', 'gray']) self._colormap_combo = ColormapCombo(color_maps) self._colormap_combo.currentIndexChanged[int].connect( self._colormap_changed) toolbar.addWidget(self._colormap_combo) self._save_button = QToolButton() self._save_button.setToolTip("Save as image") self._save_button.setIcon(resource_icon("table_export.png")) self._save_button.clicked.connect(self._save_figure) toolbar.addWidget(self._save_button) self._settings_button = QToolButton() self._settings_button.setToolTip("Toggle settings visibility") self._settings_button.setIcon(resource_icon("cog.png")) self._settings_button.setCheckable(True) self._settings_button.toggled.connect(self._show_settings) toolbar.addWidget(self._settings_button) self._help_button = QToolButton() self._help_button.setToolTip("View help") self._help_button.setIcon(resource_icon("help.png")) self._help_button.setCheckable(True) self._help_button.toggled.connect(self._show_help) toolbar.addWidget(self._help_button) def toggle_on_close(event): self._settings_button.setChecked(False) event.accept() def toggle_on_close_help(event): self._help_button.setChecked(False) event.accept() self._settings_window.closeEvent = toggle_on_close self._help_window.closeEvent = toggle_on_close_help self._colormap_combo.setCurrentIndex(45) self.set_default_layout() return toolbar def _colormap_changed(self, index): colormap = str(self._colormap_combo.itemText(index)) self._context.set_colormap(colormap) def _interpolation_changed(self, index): interpolation_name = str(self._interpolation_combo.itemText(index)) self._context.set_interpolation(interpolation_name) def _save_figure(self): formats = "Portable Network Graphic (*.png);;Adobe Acrobat (*.pdf);;Scalable Vector Graphics (*.svg)" output_file = QFileDialog.getSaveFileName(self, "Save as image", "untitled.png", formats) output_file = str(output_file).strip() if len(output_file) == 0: return image_size = self._context.image_size if not image_size: fig = self._slice_view_widget else: w, h, dpi = image_size fig = SliceViewWidget(self._context, width=w, height=h, dpi=dpi) fig.set_plot_layout( self._slice_view_widget.layout_figure().current_layout()) fig.layout_figure().savefig(output_file) def set_source_filename(self, filename): self._slice_data_source.set_source_filename(filename) def set_default_layout(self): # default slice view layout depends on the file size if self._slice_data_source.file_size < 8 * 10**8: self._layout_combo.setCurrentIndex( self._layout_combo.DEFAULT_SMALL_FILE_LAYOUT) else: self._layout_combo.setCurrentIndex( self._layout_combo.DEFAULT_LARGE_FILE_LAYOUT) def as_depth(self): self._context.samples_unit = 'Depth (m)' def _show_settings(self, toggled): self._settings_window.setVisible(toggled) if self._settings_window.isMinimized(): self._settings_window.showNormal() def _show_help(self, toggled): self._help_window.setVisible(toggled) if self._help_window.isMinimized(): self._help_window.showNormal() def show_toolbar(self, toolbar, layout_combo=True, colormap=True, save=True, settings=True): self._toolbar.setVisible(toolbar) self._colormap_combo.setDisabled(not colormap) self._save_button.setDisabled(not save) self._settings_button.setDisabled(not settings) self._layout_combo_action.setVisible(layout_combo)
class SegyTabWidget(QWidget): def __init__(self, segywidgets, show_toolbar=True, color_maps=None, width=11.7, height=8.3, dpi=100, parent=None): QWidget.__init__(self, parent) self._segywidgets = segywidgets """ :type: list[SegyViewWidget] """ self._tab_widget = QTabWidget() layout = QVBoxLayout() for i, segywidget in enumerate(self._segywidgets): segywidget.show_toolbar(False) self._tab_widget.insertTab(i, segywidget, 'File: ' + str(i + 1)) self._tab_widget.currentChanged.connect(self.update_views) slice_data_source = self.setup_datasource() inline = SliceModel("Inline", SD.inline, SD.crossline, SD.depth) xline = SliceModel("Crossline", SD.crossline, SD.inline, SD.depth) depth = SliceModel("Depth", SD.depth, SD.inline, SD.crossline) slice_models = [inline, xline, depth] self._context = SliceViewContext(slice_models, slice_data_source, has_data=False) self._context.data_changed.connect(self.data_changed) self._settings_window = SettingsWindow(self._context, self) self._toolbar = self._create_toolbar(color_maps) layout.addWidget(self._toolbar) layout.addWidget(self._tab_widget) self.setLayout(layout) def data_changed(self): dirty_models = [m for m in self._context.models if m.dirty] ctxs = [ self._tab_widget.widget(index).context for index in range(0, self._tab_widget.count()) ] local_models = list(chain.from_iterable([ctx.models for ctx in ctxs])) for m in dirty_models: for local_m in local_models: if local_m.index_direction == m.index_direction: local_m.index = m.index if local_m.x_index_direction == m.index_direction: local_m.x_index = m.index if local_m.y_index_direction == m.index_direction: local_m.y_index = m.index local_m.dirty = True m.dirty = False self.update_views() def update_views(self): self._tab_widget.currentWidget().context.load_data() def setup_datasource(self): slice_data_source = SliceDataSource(False) directions = [SD.inline, SD.crossline, SD.depth] for direction in directions: slice_data_source.set_indexes( direction, self._segywidgets[0].slice_data_source.indexes_for_direction( direction)) return slice_data_source def _create_toolbar(self, color_maps): toolbar = QToolBar() toolbar.setFloatable(False) toolbar.setMovable(False) self._settings_button = QToolButton() self._settings_button.setToolTip("Toggle settings visibility") self._settings_button.setIcon(resource_icon("cog.png")) self._settings_button.setCheckable(True) self._settings_button.toggled.connect(self._show_settings) toolbar.addWidget(self._settings_button) def toggle_on_close(event): self._settings_button.setChecked(False) event.accept() self._settings_window.closeEvent = toggle_on_close return toolbar def _show_settings(self, toggled): self._settings_window.setVisible(toggled) if self._settings_window.isMinimized(): self._settings_window.showNormal()