Example #1
0
    def __init__(self, parent=None):

        super(FitsViewer, self).__init__(parent)

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.current_file = None
        self._do_read_settings = True

        self.file_model = QtGui.QFileSystemModel()

        if len(sys.argv) == 2 and os.path.isfile(sys.argv[1]):
            components = sys.argv[1].split('/')
            start_dir = '/'.join(components[:-1])
            self.preselect_file = sys.argv[1]

        elif len(sys.argv) == 2 and os.path.isdir(sys.argv[1]):
            start_dir = sys.argv[1]
            self.preselect_file = None

        else:
            start_dir = os.getcwd()
            self.preselect_file = None

        if not os.path.isdir(start_dir):
            start_dir = os.getcwd()

        self.file_model.setRootPath(start_dir)
        self.file_model.setFilter(
            QtCore.QDir.Files | QtCore.QDir.Dirs | QtCore.QDir.NoDot
            )
        self.file_model.directoryLoaded.connect(self.on_dir_loaded)
        self.ui.files.setModel(self.file_model)

        self.ui.url.setText(start_dir)
        self.ui.url.textChanged.connect(self.on_path_changed)
        self.on_path_changed()
        self.ui.files.selectionModel().selectionChanged.connect(
            self.on_file_selected
            )

        self.ui.sections.setSelectionBehavior(QtGui.QTableView.SelectRows)
        self.ui.sections.verticalHeader().setDefaultSectionSize(20)
        self.ui.sections.setShowGrid(False)
        self.ui.header.setShowGrid(False)
        self.ui.sections.setStyleSheet('QTableView::item { padding: 25px }')
        self.ui.header.setStyleSheet('QTableView::item { padding: 8px }')
        self.ui.contents.setStyleSheet('QTableView::item { padding: 8px }')

        # self.ui.splitter.setSizes([200, 500])
        # self.ui.splitter_2.setSizes([200, 500])
        # self.ui.splitter_3.setSizes([200, 500])

        menu = QtGui.QMenu()

        for action_name, action_args in PLOT_ACTIONS:
            action = QtGui.QAction(action_name, menu)
            action.setProperty('args', action_args)
            action.triggered.connect(self.on_plot_selection_triggered)
            menu.addAction(action)
            if not self.ui.plotButton.defaultAction():
                self.ui.plotButton.setDefaultAction(action)

        self.ui.plotButton.setMenu(menu)
        self.ui.indicesCheckbox.toggled.connect(self.on_indices_toggled)

        self.ui.contents.pressed.connect(self.on_plot_selection_triggered)

        self.ui.filterHeader.textChanged.connect(self.on_header_filter_changed)
        self.ui.filterData.textChanged.connect(self.on_data_filter_changed)
        self.ui.filterFiles.textChanged.connect(self.on_files_filter_changed)
        self.ui.filterSections.textChanged.connect(
            self.on_hduList_filter_changed
            )
        self.ui.browseDirectoryButton.clicked.connect(
            self.on_browsedir_clicked
            )

        self.data_filter_timer = QtCore.QTimer()
        self.data_filter_timer.setSingleShot(True)
        self.data_filter_timer.setInterval(300)
        self.data_filter_timer.timeout.connect(self.change_data_filter)

        self.active_plot_window = None
Example #2
0
class FitsViewer(QtGui.QMainWindow):

    def __init__(self, parent=None):

        super(FitsViewer, self).__init__(parent)

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.current_file = None
        self._do_read_settings = True

        self.file_model = QtGui.QFileSystemModel()

        if len(sys.argv) == 2 and os.path.isfile(sys.argv[1]):
            components = sys.argv[1].split('/')
            start_dir = '/'.join(components[:-1])
            self.preselect_file = sys.argv[1]

        elif len(sys.argv) == 2 and os.path.isdir(sys.argv[1]):
            start_dir = sys.argv[1]
            self.preselect_file = None

        else:
            start_dir = os.getcwd()
            self.preselect_file = None

        if not os.path.isdir(start_dir):
            start_dir = os.getcwd()

        self.file_model.setRootPath(start_dir)
        self.file_model.setFilter(
            QtCore.QDir.Files | QtCore.QDir.Dirs | QtCore.QDir.NoDot
            )
        self.file_model.directoryLoaded.connect(self.on_dir_loaded)
        self.ui.files.setModel(self.file_model)

        self.ui.url.setText(start_dir)
        self.ui.url.textChanged.connect(self.on_path_changed)
        self.on_path_changed()
        self.ui.files.selectionModel().selectionChanged.connect(
            self.on_file_selected
            )

        self.ui.sections.setSelectionBehavior(QtGui.QTableView.SelectRows)
        self.ui.sections.verticalHeader().setDefaultSectionSize(20)
        self.ui.sections.setShowGrid(False)
        self.ui.header.setShowGrid(False)
        self.ui.sections.setStyleSheet('QTableView::item { padding: 25px }')
        self.ui.header.setStyleSheet('QTableView::item { padding: 8px }')
        self.ui.contents.setStyleSheet('QTableView::item { padding: 8px }')

        # self.ui.splitter.setSizes([200, 500])
        # self.ui.splitter_2.setSizes([200, 500])
        # self.ui.splitter_3.setSizes([200, 500])

        menu = QtGui.QMenu()

        for action_name, action_args in PLOT_ACTIONS:
            action = QtGui.QAction(action_name, menu)
            action.setProperty('args', action_args)
            action.triggered.connect(self.on_plot_selection_triggered)
            menu.addAction(action)
            if not self.ui.plotButton.defaultAction():
                self.ui.plotButton.setDefaultAction(action)

        self.ui.plotButton.setMenu(menu)
        self.ui.indicesCheckbox.toggled.connect(self.on_indices_toggled)

        self.ui.contents.pressed.connect(self.on_plot_selection_triggered)

        self.ui.filterHeader.textChanged.connect(self.on_header_filter_changed)
        self.ui.filterData.textChanged.connect(self.on_data_filter_changed)
        self.ui.filterFiles.textChanged.connect(self.on_files_filter_changed)
        self.ui.filterSections.textChanged.connect(
            self.on_hduList_filter_changed
            )
        self.ui.browseDirectoryButton.clicked.connect(
            self.on_browsedir_clicked
            )

        self.data_filter_timer = QtCore.QTimer()
        self.data_filter_timer.setSingleShot(True)
        self.data_filter_timer.setInterval(300)
        self.data_filter_timer.timeout.connect(self.change_data_filter)

        self.active_plot_window = None

    def on_indices_toggled(self, state):

        self.ui.arrayIndices.setEnabled(state)

    def on_dir_loaded(self, directory):

        # we only want this to be called once on startup
        self.file_model.directoryLoaded.disconnect(self.on_dir_loaded)
        if self.preselect_file is None:
            return
        # preselect the given file
        index = self.file_model.index(self.preselect_file)
        self.ui.files.selectionModel().select(
            index, QtGui.QItemSelectionModel.ClearAndSelect
            )

    def on_browsedir_clicked(self):

        directory = QtGui.QFileDialog.getExistingDirectory(
            None, 'Select base FITS directory', self.ui.url.text()
            )
        if len(directory):
            self.ui.url.setText(directory)

    def on_hduList_filter_changed(self, new_text):

        self.hduList_proxy_model.setFilterRegExp(QtCore.QRegExp(
            new_text, Qt.CaseInsensitive, QtCore.QRegExp.RegExp2
            ))

    def on_files_filter_changed(self, new_text):

        self.file_model.setNameFilters([
            '*{}*'.format(item)
            for item in str(new_text).split()
            ])
        self.file_model.setNameFilterDisables(False)

    def on_hdu_selection_changed(self, item):

        real_item = self.hduList_proxy_model.mapToSource(item)
        hdu_entry = self.hduList_model.hdu_entry_for_index(real_item)

        self.hdu_header_proxy_model = QtGui.QSortFilterProxyModel()
        self.hdu_header_proxy_model.setSourceModel(FitsHeaderModel(hdu_entry))
        self.hdu_header_proxy_model.setFilterKeyColumn(0)
        self.ui.header.setModel(self.hdu_header_proxy_model)
        self.ui.header.resizeColumnsToContents()
        self.ui.header.verticalHeader().setDefaultSectionSize(20)
        self.on_header_filter_changed(self.ui.filterHeader.text())

        self.hdu_data_proxymodel = DataSortFilterProxyModel()
        self.hdu_data_proxymodel.setSourceModel(FitsDataModel(hdu_entry))
        self.ui.contents.setModel(self.hdu_data_proxymodel)
        self.ui.contents.resizeColumnsToContents()
        self.ui.contents.verticalHeader().setDefaultSectionSize(20)
        self.change_data_filter()

    def on_plot_selection_triggered(self):

        if (
                not QtGui.QApplication.mouseButtons() & Qt.RightButton and
                not isinstance(self.sender(), QtGui.QPushButton) and
                not isinstance(self.sender(), QtGui.QAction)
                ):

            return

        plot_args = {}
        if hasattr(self.sender(), 'text'):
            try:
                plot_args = list(filter(
                    lambda x: x[0] == self.sender().text(),
                    PLOT_ACTIONS
                    ))[0][1]
            except IndexError:
                pass

        if (
                self.active_plot_window is None or
                self.active_plot_window.isHidden()
                ):
            self.active_plot_window = PlotWindow()

        p = self.active_plot_window.active_plot()
        # needs to be called here so the hold state is correct for the
        # selected subplot
        self.active_plot_window.update_hold()

        selection = self.ui.contents.selectionModel().selectedIndexes()

        if self.ui.indicesCheckbox.isChecked():
            array_fields = self.ui.arrayIndices.value()
        else:
            array_fields = '*'

        def _get_data(index):

            index = self.hdu_data_proxymodel.mapToSource(index)
            data = self.hdu_data_proxymodel.sourceModel().data(
                index, RAW_DATA_ROLE
                )

            if type(data) == np.ndarray and array_fields != '*':

                try:
                    return data[array_fields]
                except ValueError:
                    self.ui.statusbar.showMessage(
                        'out-of-range value in array index field', 10000
                        )

            return data

        def _get_label(column):

            return str(self.hdu_data_proxymodel.sourceModel().headerData(
                column, Qt.Horizontal, Qt.DisplayRole
                ))

        unique_cols_selected = set(item.column() for item in selection)

        if len(selection) == 1:

            p.set_ylabel(_get_label(selection[0].column()))
            p.plot(_get_data(selection[0]), **plot_args)

        elif len(unique_cols_selected) == 2:

            # plot first as x, second as y
            key_col, value_col = unique_cols_selected
            keys = [
                _get_data(index)
                for index in selection
                if index.column() == key_col
                ]
            values = [
                _get_data(index)
                for index in selection
                if index.column() == value_col
                ]
            if np.ndarray in map(type, keys) or np.ndarray in map(type, values):
                self.ui.statusbar.showMessage(
                    "Sorry, don't know how to plot that", 10000
                    )
                return

            p.set_xlabel(_get_label(key_col))
            p.set_ylabel(_get_label(value_col))
            p.plot(keys, values, **plot_args)

        else:

            data_parts = [_get_data(index) for index in selection]
            label = ', '.join(set(
                _get_label(index.column())
                for index in selection
                ))
            p.set_ylabel(label)

            if False not in [
                    isinstance(part, np.ndarray)
                    for part in data_parts
                    ]:
                # all items are arrays -> plot as curves
                for item in data_parts:
                    p.plot(item, **plot_args)
                    # temporarily enable hold so all curves are painted
                    # this is inside the loop so the first call clears the
                    # plot if desired
                    self.active_plot_window.canvas.axes.hold(True)
                self.active_plot_window.update_hold()
            else:
                p.plot(np.array(data_parts), **plot_args)

        if self.active_plot_window.isVisible():
            self.active_plot_window.active_plot().figure.canvas.draw()
        else:
            self.active_plot_window.show()

        self.active_plot_window.make_next_subplot_current()

    def on_header_filter_changed(self, new_text):
        self.hdu_header_proxy_model.setFilterRegExp(QtCore.QRegExp(
            new_text, Qt.CaseInsensitive, QtCore.QRegExp.RegExp2
            ))

    def on_data_filter_changed(self, new_text):

        self.data_filter_timer.text = new_text
        self.data_filter_timer.start()

    def change_data_filter(self):

        if not hasattr(self.data_filter_timer, 'text'):
            return

        self.hdu_data_proxymodel.change_filter(self.data_filter_timer.text)

    def on_file_selected(self):

        files = self.ui.files.selectionModel().selectedIndexes()

        try:
            filename = str(files[0].data(Qt.DisplayRole).toString())
        except AttributeError:
            filename = str(files[0].data(Qt.DisplayRole))
        except IndexError:
            print('No file selected, huh?')
            return

        new_path = self.ui.url.text() + '/' + filename

        if os.path.isdir(new_path):
            # If the item which was clicked was a directory, go there
            self.ui.url.setText(os.path.normpath(str(new_path)))
            return

        self.current_file = new_path

        try:
            hdulist = pyfits.open(str(self.current_file))
        except IOError as e:
            self.ui.statusbar.showMessage(
                'Failed to open {}: {}'.format(self.current_file, str(e)),
                10000
                )
            return

        self.hduList_proxy_model = QtGui.QSortFilterProxyModel()
        self.hduList_model = FitsHeaderListModel(hdulist)
        self.hduList_proxy_model.setSourceModel(self.hduList_model)
        self.on_hduList_filter_changed(self.ui.filterSections.text())

        self.ui.sections.setModel(self.hduList_proxy_model)
        self.ui.sections.resizeColumnsToContents()
        self.ui.sections.selectionModel().currentChanged.connect(
            self.on_hdu_selection_changed
            )
        self.ui.header.setModel(None)
        self.ui.contents.setModel(None)
        self.setWindowTitle('pyfv: {}'.format(self.current_file))

    def on_path_changed(self):

        index = self.file_model.setRootPath(self.ui.url.text())
        self.ui.files.setRootIndex(index)
        self.setWindowTitle('pyfv: {}'.format(self.ui.url.text()))

    def closeEvent(self, event):

        self.write_settings()
        super(FitsViewer, self).closeEvent(event)

    def write_settings(self):
        '''
        Store settings (including layout and window geometry).
        '''

        settings = QtCore.QSettings('RadioTeleskopEffelsberg', 'pyfv')
        settings.setValue(
            'splitter/splitterSizes', self.ui.splitter.saveState()
            )
        settings.setValue(
            'splitter_2/splitterSizes', self.ui.splitter_2.saveState()
            )
        settings.setValue(
            'splitter_3/splitterSizes', self.ui.splitter_3.saveState()
            )
        settings.setValue('pyfv/geometry', self.saveGeometry())
        settings.setValue('pyfv/windowState', self.saveState())

    def read_settings(self):
        '''
        Read stored settings (including layout and window geometry).
        '''

        settings = QtCore.QSettings('RadioTeleskopEffelsberg', 'pyfv')

        self.ui.splitter.restoreState(settings.value('splitter/splitterSizes'))
        self.ui.splitter_2.restoreState(
            settings.value('splitter_2/splitterSizes')
            )
        self.ui.splitter_3.restoreState(
            settings.value('splitter_3/splitterSizes')
            )
        self.restoreGeometry(settings.value('pyfv/geometry'))
        self.restoreState(settings.value('pyfv/windowState'))

    def showEvent(self, se):
        '''
        it is necessary to perform "readSettings" after all of the GUI elements
        were processed and the first showevent occurs otherwise not all
        settings will be processed correctly
        '''

        super(FitsViewer, self).showEvent(se)

        if self._do_read_settings:
            self.read_settings()
            self._do_read_settings = False