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
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