commit 567a37e08c2cb163f5efaed912fd1fe8817a1bb7 Author: Santiago Soler Date: Wed Jan 31 12:35:47 2018 -0300 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/figure_canvas.py b/figure_canvas.py new file mode 100644 index 0000000..42193e5 --- /dev/null +++ b/figure_canvas.py @@ -0,0 +1,95 @@ +from __future__ import print_function +from future.builtins import super + +import os +import sys +import numpy +import matplotlib +from matplotlib.figure import Figure +from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg +from PyQt5.QtGui import QIcon +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import QSizePolicy, QMainWindow, QApplication, QAction +from PyQt5.QtWidgets import QMenu, QWidget, QVBoxLayout, QMessageBox +from PyQt5.QtWidgets import QSlider, QHBoxLayout, QLabel, QDialog +from PyQt5.QtWidgets import QDialogButtonBox + + +class GravityModelCanvas(FigureCanvasQTAgg): + + def __init__(self, parent=None, width=5, height=4, dpi=100): + self.fig = Figure(figsize=(width, height), dpi=dpi) + super().__init__(self.fig) + self.setParent(parent) + + self._x, self._z = None, None + self._min_depth, self._max_depth = 0., 35000. + + self.predicted = None + self.data = None + self.polygons = None + + self._plotted = False + + FigureCanvasQTAgg.setSizePolicy(self, + QSizePolicy.Expanding, + QSizePolicy.Expanding) + FigureCanvasQTAgg.updateGeometry(self) + + @property + def x(self): + return self._x + + @x.setter + def x(self, new_value): + self._x = new_value + + @property + def z(self): + return self._z + + @z.setter + def z(self, new_value): + self._z = new_value + + @property + def min_depth(self): + return self._min_depth + + @min_depth.setter + def min_depth(self, new_value): + self._min_depth = new_value + + @property + def max_depth(self): + return self._max_depth + + @max_depth.setter + def max_depth(self, new_value): + self._max_depth = new_value + + def update_plot(self): + if self._plotted: + pass + else: + self._figure_setup() + + def _figure_setup(self, **kwargs): + self.dataax, self.modelax = self.fig.subplots(2, 1, sharex=True) + if self.data is not None: + self.data_line, = self.dataax.plot(self.x, self.data, '.k') + self.dataax.set_ylabel('Gravity anomaly [mGal]') + self.dataax.set_xlim(self.x.min(), self.x.max()) + self.dataax.set_ylim((-200, 200)) + self.dataax.grid(True) + self.modelax.set_xlabel('x [m]') + self.modelax.set_xlim(self.x.min(), self.x.max()) + self.modelax.set_ylim(self.min_depth, self.max_depth) + self.modelax.grid(True) + self.modelax.invert_yaxis() + self.modelax.set_ylabel('z [m]') + self.fig.subplots_adjust(top=0.95, left=0.1, right=0.95, bottom=0.1, + hspace=0.1) + self.figure = self.fig + self.canvas = self.fig.canvas + self.fig.canvas.draw() diff --git a/moulder.py b/moulder.py new file mode 100644 index 0000000..25a222a --- /dev/null +++ b/moulder.py @@ -0,0 +1,110 @@ +from __future__ import print_function +from future.builtins import super + +import os +import sys +import numpy as np +import matplotlib +from matplotlib.figure import Figure +from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg +from PyQt5.QtGui import QIcon +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import QSizePolicy, QMainWindow, QApplication, QAction +from PyQt5.QtWidgets import QMenu, QWidget, QVBoxLayout, QMessageBox +from PyQt5.QtWidgets import QSlider, QHBoxLayout, QLabel, QDialog +from PyQt5.QtWidgets import QDialogButtonBox + +from figure_canvas import GravityModelCanvas +from new_dialog import NewModelDialog + + +class Moulder(QMainWindow): + + def __init__(self): + super().__init__() + self.setWindowTitle("Moulder") + self.setWindowIcon(QIcon.fromTheme('python-logo')) + self.setGeometry(200, 200, 1024, 800) + self.init_ui() + self.set_callbacks() + + self.canvas = GravityModelCanvas(self, + width=5, height=4, dpi=100) + self.canvas.setFocus() + self.setCentralWidget(self.canvas) + + def closeEvent(self, event): + event.ignore() + self._quit_callback() + + def init_ui(self): + self._define_actions() + self._configure_menubar() + self._configure_toolbar() + + def set_callbacks(self): + self.new_action.triggered.connect(self._new_model_callback) + self.about_action.triggered.connect(self._about_callback) + # self.file_menu.triggered.connect(self._file_menu_callback) + self.quit_action.triggered.connect(self._quit_callback) + + def _define_actions(self): + self.new_action = QAction(QIcon.fromTheme('document-new'), + '&New model', self) + self.new_action.setShortcut('Ctrl+N') + self.open_action = QAction(QIcon.fromTheme('document-open'), + '&Open model', self) + self.open_action.setShortcut('Ctrl+O') + self.save_action = QAction(QIcon.fromTheme('document-save'), + '&Save model', self) + self.save_action.setShortcut('Ctrl+S') + self.save_as_action = QAction(QIcon.fromTheme('document-save-as'), + '&Save model as...', self) + self.save_as_action.setShortcut('Ctrl+Shift+S') + self.quit_action = QAction(QIcon.fromTheme('application-exit'), + '&Quit', self) + self.quit_action.setShortcut('Ctrl+Q') + self.about_action = QAction("&About", self) + + def _configure_menubar(self): + self.menubar = self.menuBar() + self.file_menu = self.menubar.addMenu('File') + self.file_menu.addAction(self.open_action) + self.file_menu.addAction(self.save_action) + self.file_menu.addAction(self.quit_action) + self.about_menu = self.menubar.addMenu('About') + self.about_menu.addAction(self.about_action) + + def _configure_toolbar(self): + self.toolbar = self.addToolBar("adasd") + self.toolbar.addAction(self.new_action) + self.toolbar.addAction(self.open_action) + self.toolbar.addAction(self.save_action) + self.toolbar.addAction(self.save_as_action) + + def _about_callback(self): + QMessageBox.about(self, "About Moulder", + "About Moulder\nVersion 0.1") + + def _new_model_callback(self): + new_model_dialog = NewModelDialog(parent=self) + new_model_dialog.exec_() + if new_model_dialog.is_completed(): + self.canvas.x = new_model_dialog.x + self.canvas.z = new_model_dialog.z + self.canvas.update_plot() + + def _quit_callback(self): + answer = QMessageBox.question(self, "Quit", + "Are you sure you want to quit?", + QMessageBox.Yes, QMessageBox.No) + if answer == QMessageBox.Yes: + sys.exit() + + +if __name__ == "__main__": + app = QApplication(sys.argv) + app.setApplicationName("Moulder") + moulder = Moulder() + moulder.show() + sys.exit(app.exec_()) diff --git a/new_dialog.py b/new_dialog.py new file mode 100644 index 0000000..28da1cb --- /dev/null +++ b/new_dialog.py @@ -0,0 +1,147 @@ +from __future__ import print_function +from future.builtins import super + +import os +import sys +import numpy +import matplotlib +from matplotlib.figure import Figure +from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg +from PyQt5.QtGui import QIcon, QFont +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import QSizePolicy, QMainWindow, QApplication, QAction +from PyQt5.QtWidgets import QMenu, QWidget, QVBoxLayout, QMessageBox +from PyQt5.QtWidgets import QSlider, QHBoxLayout, QLabel, QDialog, QPushButton +from PyQt5.QtWidgets import QDialogButtonBox, QGridLayout, QRadioButton, QLineEdit + + +class NewModelDialog(QDialog): + + def __init__(self, parent=None): + super().__init__(parent) + self.setModal(False) + self.setWindowTitle("Create New Model") + self._completed = False + self._init_ui() + + self.regular_grid_btn.toggled.connect(self._radio_button_callback) + self.custom_grid_btn.toggled.connect(self._radio_button_callback) + self.cancel_btn.clicked.connect(self._button_pushed_callback) + self.ok_btn.clicked.connect(self._button_pushed_callback) + + @property + def x(self): + if self.regular_grid_btn.isChecked(): + entries = self._read_regular_grid_entries() + if entries: + x1, x2, step, z = entries[:] + return numpy.arange(x1, x2, step, dtype=numpy.float64) + else: + return None + elif self.custom_grid_btn.isChecked(): + # Need to be completed + pass + + @property + def z(self): + if self.regular_grid_btn.isChecked(): + entries = self._read_regular_grid_entries() + if entries: + x1, x2, step, z = entries[:] + return z*numpy.ones_like(self.x) + else: + return None + elif self.custom_grid_btn.isChecked(): + # Need to be completed + pass + + def is_completed(self): + return self._completed + + def _init_ui(self): + self.regular_grid_btn = QRadioButton("Regular grid (in meters)") + self.regular_grid_btn.setChecked(True) + self.custom_grid_btn = QRadioButton("Custom grid") + self.from_input = QLineEdit() + self.to_input = QLineEdit() + self.step_input = QLineEdit() + self.height_input = QLineEdit() + self.ok_btn = QPushButton("Ok") + self.cancel_btn = QPushButton("Cancel") + self.ok_btn.setDefault(True) + + bold_font = QFont() + bold_font.setBold(True) + + layout = QVBoxLayout() + layout.addWidget(QLabel("Create Meassurement Points", font=bold_font)) + layout.addWidget(self.regular_grid_btn) + + grid = QGridLayout() + grid.setContentsMargins(25, 0, 0, 0) + grid.addWidget(QLabel("From:"), 0, 0) + grid.addWidget(self.from_input, 0, 1) + grid.addWidget(QLabel("To:"), 0, 2) + grid.addWidget(self.to_input, 0, 3) + grid.addWidget(QLabel("Step:"), 0, 4) + grid.addWidget(self.step_input, 0, 5) + grid.addWidget(QLabel("Height:"), 1, 0) + grid.addWidget(self.height_input, 1, 1, 1, 5) + layout.addLayout(grid) + + layout.addWidget(self.custom_grid_btn) + + hbox = QHBoxLayout() + hbox.setAlignment(Qt.AlignRight) + hbox.addWidget(self.cancel_btn) + hbox.addWidget(self.ok_btn) + layout.addLayout(hbox) + + self.setLayout(layout) + + def _button_pushed_callback(self): + sender_text = self.sender().text() + if sender_text == "Cancel": + self.close() + elif sender_text == "Ok": + filled_entries = self._check_filled_entries() + if filled_entries: + self._completed = True + self.close() + else: + QMessageBox.warning(self, "Warning", + "Some entries are not properly " + + "completed or are incomplete.") + + def _radio_button_callback(self): + regular_grid_lines = [self.from_input, self.to_input, + self.step_input, self.height_input] + if self.sender().text() == "Custom grid": + for line_edit in regular_grid_lines: + line_edit.setDisabled(True) + else: + for line_edit in regular_grid_lines: + line_edit.setEnabled(True) + + def _check_filled_entries(self): + if self.regular_grid_btn.isChecked(): + entries = self._read_regular_grid_entries() + if entries: + return True + else: + # Show messagebox with warning for not completed entries + pass + elif self.custom_grid_btn.isChecked(): + # Needed to be completed + return False + + def _read_regular_grid_entries(self): + x1, x2 = self.from_input.text(), self.to_input.text() + step = self.step_input.text() + z = self.height_input.text() + try: + x1, x2, step = float(x1), float(x2), float(step) + z = float(z) + except ValueError: + return False + return x1, x2, step, z