def on_subtractBtn_clicked(self): from ufit.gui.scanitem import ScanDataItem dlg = QDialog(self) loadUi(dlg, 'subtract.ui') data2obj = dlg.setList.populate(ScanDataItem) if dlg.exec_() != QDialog.Accepted: return witems = dlg.setList.selectedItems() if not witems: return try: prec = float(dlg.precisionEdit.text()) except ValueError: QMessageBox.warning(self, 'Error', 'Please enter a valid precision.') return new_data = self.data.subtract(data2obj[witems[0].type()].data, prec, dlg.destructBox.isChecked()) if not dlg.destructBox.isChecked(): new_model = self.model.copy() from ufit.gui.scanitem import ScanDataItem session.add_item(ScanDataItem(new_data, new_model), self.item.group) else: self.replotRequest.emit(None) session.set_dirty()
def _update_settings(self): """Update settings from controls.""" s = self.item.settings old_title = s.title title = s.title = self.titleBox.text() if title != old_title: self.item.update_htmldesc() xaxis = str(self.xaxisBox.currentText()) yaxis = str(self.yaxisBox.currentText()) if xaxis != s.xaxis or yaxis != s.yaxis: # new axes selected => discard limits on replot self.mapdata = None if xaxis == yaxis: QMessageBox.warning(self, 'Error', 'Please select distinct X ' 'and Y axes.') return s.xaxis = xaxis s.yaxis = yaxis s.interp = self.stepBox.value() s.zmin = maybe_float(self.zminEdit.text(), -1e300) s.zmax = maybe_float(self.zmaxEdit.text(), 1e300) s.yscale = maybe_float(self.scaleEdit.text(), 1.0) s.usemask = self.usemaskBox.isChecked() s.dots = self.dotsBox.isChecked() s.contour = self.contourBox.isChecked() s.logz = self.logBox.isChecked() s.gauss2d = self.fitBox.isChecked() session.set_dirty()
def on_fftBtn_clicked(self): try: npoints = int(self.fftNpointsEdit.text()) except ValueError: QMessageBox.warning(self, 'Error', 'Please enter a valid number of points.') return xmin = self.data.x.min() xmax = self.data.x.max() xinterp = linspace(xmin, xmax, npoints) yinterp = interp1d(self.data.x, self.data.y, kind='linear') yfft = fft(yinterp(xinterp)) p2 = abs(yfft) / npoints p1 = p2[:npoints // 2 + 2] p1[1:-1] *= 2 dx = (xmax - xmin) / (npoints - 1) new_data = ScanData.from_arrays(name='FFT(' + self.data.name + ')', x=(1. / dx) * arange(npoints // 2 + 2) / npoints, y=p1, dy=0.01 * ones(p1.shape), xcol='1/' + self.data.xaxis, ycol='|P1|') new_model = self.model.copy() from ufit.gui.scanitem import ScanDataItem session.add_item(ScanDataItem(new_data, new_model), self.item.group)
def on_mergeBtn_clicked(self): try: precision = float(self.mergeEdit.text()) except ValueError: QMessageBox.warning(self, 'Error', 'Enter a valid precision.') return new_data = self.datas[0].merge(precision, *self.datas[1:]) session.add_item(ScanDataItem(new_data), self.items[-1].group)
def save_session(self): if session.filename is None: return self.save_session_as() try: session.save() except Exception as err: logger.exception('Saving session failed') QMessageBox.warning(self, 'Error', 'Saving failed: %s' % err) return False return True
def on_actionExportParams_triggered(self): items = self.selected_items(ScanDataItem) dlg = ParamExportDialog(self, items) if dlg.exec_() != QDialog.Accepted: return expfilename = self._get_export_filename() if expfilename: try: dlg.do_export(expfilename) except Exception as e: logger.exception('While exporting parameters') QMessageBox.warning(self, 'Error', 'Could not export ' 'parameters: %s' % e)
def on_tbl_itemChanged(self, item): if self._updating: return try: new_value = eval(str(item.text())) except Exception: QMessageBox.error(self, 'Error', 'The new value is not a valid expression.') return else: key = str(self.tbl.item(item.row(), 0).text()) self.data.meta[key] = new_value self.replotRequest.emit(None) session.set_dirty()
def save_session_as(self): initialdir = session.dirname filename, _ = QFileDialog.getSaveFileName(self, 'Select file name', initialdir, 'ufit files (*.ufit)') if filename == '': return False session.set_filename(path_to_str(filename)) try: session.save() except Exception as err: logger.exception('Saving session failed') QMessageBox.warning(self, 'Error', 'Saving failed: %s' % err) return False return True
def on_rebinBtn_clicked(self): try: binsize = float(self.precisionEdit.text()) except ValueError: QMessageBox.warning(self, 'Error', 'Enter a valid precision.') return new_array, new_meta = rebin(self.data._data, binsize, self.data.meta) self.data.__init__(new_meta, new_array, self.data.xcol, self.data.ycol, self.data.ncol, self.data.nscale, name=self.data.name, sources=self.data.sources) self.replotRequest.emit(None) session.set_dirty()
def on_numorHelpBtn_clicked(self): QMessageBox.information( self, 'Numor Help', '''\ The numor string contains file numbers, with the following operators: , loads multiple files - loads multiple sequential files + merges multiple files > merges multiple sequential files For example: * 10-15,23 loads files 10 through 15 and 23 in 7 separate datasets. * 10+11,23+24 loads two datasets consisting of files 10 and 11 merged \ into one set, as well as files 23 and 24. * 10>15+23 merges files 10 through 15 and 23 into one single dataset. * 10,11,12+13,14 loads four sets. ''')
def do_fit(self): if self.picking: QMessageBox.information(self, 'Fitting', 'Please finish the picking operation first.') return self.update_from_controls() self.statusLabel.setText('Working...') self.statusLabel.repaint() QApplication.processEvents() try: res = self.model.fit(self.data, **self.fit_kws) except Exception as e: self.logger.exception('Error during fit') self.statusLabel.setText('Error during fit: %s' % e) return self.on_modelFitted(self.item, res) self.replotRequest.emit(True) session.set_dirty()
def on_actionRemoveData_triggered(self): items = self.selected_items() if not items: return if QMessageBox.question( self, 'ufit', 'OK to remove %d item(s)?' % len(items), QMessageBox.Yes | QMessageBox.No) == QMessageBox.No: return session.remove_items(items) self.re_expand_tree()
def eval_model(self, final=False): modeldef = str(self.modeldefEdit.toPlainText()).replace('\n', ' ') if not modeldef: QMessageBox.information(self, 'Error', 'No model defined.') return try: model = eval_model(modeldef) except Exception as e: self.logger.exception('Could not evaluate model') QMessageBox.information(self, 'Error', 'Could not evaluate model: %s' % e) return if final: self._finish_picking() self.last_model = model self.newModel.emit(model, False, True) self.closeRequest.emit() else: self.statusLbl.setText('Model definition is good.')
def exec_callback(self): try: from ufit.gui.console import ConsoleWindow except ImportError: logger.exception('Qt console window cannot be opened without ' 'IPython; import error was:') QMessageBox.information( self, 'ufit', 'Please install IPython with qtconsole to ' 'activate this function.') return w = ConsoleWindow(self) w.ipython.executeCommand('from ufit.lab import *') w.ipython.pushVariables({ 'fig': self.canvas.figure, 'ax': self.canvas.figure.gca(), 'D': [item for group in session.groups for item in group.items], }) w.show()
def check_save(self): if not self.isWindowModified(): # nothing there to be saved return True resp = QMessageBox.question( self, 'ufit', 'Save current session?\n%s' % session.filename, QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) if resp == QMessageBox.Yes: return self.save_session() elif resp == QMessageBox.No: return True return False
def set_template(self, dtempl, numor, silent=True): self.templateEdit.setText(str_to_path(dtempl)) with self.sgroup as settings: settings.setValue('last_data_template', dtempl) self.loader.template = dtempl try: cols, xguess, yguess, dyguess, mguess, nmon = \ self.loader.guess_cols(numor) except Exception as e: if not silent: self.logger.exception('Could not read column names') QMessageBox.information(self, 'Error', 'Could not read column names: %s' % e) return self.xcolBox.clear() self.xcolBox.addItem('auto') self.xcolBox.setCurrentIndex(0) self.ycolBox.clear() self.ycolBox.addItem('auto') self.ycolBox.setCurrentIndex(0) self.dycolBox.clear() self.dycolBox.addItem('auto') self.dycolBox.addItem('sqrt(Y)') self.dycolBox.setCurrentIndex(0) self.moncolBox.clear() self.moncolBox.addItem('auto') self.moncolBox.addItem('none') self.moncolBox.setCurrentIndex(0) self.filtercolBox.clear() self.filtercolBox.addItem('none') self.filtercolBox.setCurrentIndex(0) for i, name in enumerate(cols): self.xcolBox.addItem(name) self.ycolBox.addItem(name) self.dycolBox.addItem(name) self.moncolBox.addItem(name) self.filtercolBox.addItem(name) self.monscaleEdit.setText(str(nmon or 1)) self.numorsEdit.setText(str(numor)) self.open_data()
def on_actionReorder_triggered(self): dlg = QDialog(self) loadUi(dlg, 'reorder.ui') data2obj = dlg.itemList.populate() if not dlg.exec_(): return new_structure = [] for i in range(dlg.itemList.count()): new_index = dlg.itemList.item(i).type() obj = data2obj[new_index] if isinstance(obj, ItemGroup): new_structure.append((obj, [])) else: if not new_structure: QMessageBox.warning( self, 'ufit', 'Reordering invalid: every data item ' 'must be below a group') return new_structure[-1][1].append(obj) session.reorder_groups(new_structure) self.re_expand_tree()
def load_session(self, filename=None): if not filename: # Recent files action action = self.sender() if isinstance(action, QAction): if not self.check_save(): return filename = action.data() try: session.load(filename) with self.sgroup as settings: settings.setValue('loadfiledirectory', path.dirname(filename)) except Exception as err: logger.exception('Loading session %r failed' % filename) QMessageBox.warning(self, 'Error', 'Loading failed: %s' % err) else: self.re_expand_tree() self.setWindowModified(False) # if there are annotations, show the window automatically if session.props.get('annotations'): self.on_actionAnnotations_triggered()
def on_addCustomBtn_clicked(self): dlg = QDialog(self) loadUi(dlg, 'custommodel.ui') while 1: if dlg.exec_() != QDialog.Accepted: return modelname = str(dlg.nameBox.text()) params = str(dlg.paramBox.text()) value = str(dlg.valueEdit.toPlainText()).strip() if not ident_re.match(modelname): QMessageBox.warning( self, 'Error', 'Please enter a valid model ' 'name (must be a Python identifier using ' 'only alphabetic characters and digits).') continue if not params: QMessageBox.warning(self, 'Error', 'Please enter some parameters.') continue for param in params.split(): if not ident_re.match(param): QMessageBox.warning( self, 'Error', 'Parameter name %s is not valid (must ' 'be a Python identifier using only alphabetic ' 'characters and digits).' % param) params = None break if not params: continue break self.insert_model_code('Custom(%r, %r, %r)' % (modelname, params, value))
def save_figure(self, *args): filetypes = self.canvas.get_supported_filetypes_grouped() sorted_filetypes = sorted(filetypes.items()) start = self.canvas.get_default_filename() filters = [] for name, exts in sorted_filetypes: if 'eps' in exts or 'emf' in exts or 'jpg' in exts or \ 'pgf' in exts or 'raw' in exts: continue exts_list = " ".join(['*.%s' % ext for ext in exts]) filter = '%s (%s)' % (name, exts_list) filters.append(filter) filters = ';;'.join(filters) fname, _ = QFileDialog.getSaveFileName(self, 'Choose a filename to save to', start, filters) if fname: try: self.canvas.print_figure(text_type(fname)) except Exception as e: logger.exception('Error saving file') QMessageBox.critical(self, 'Error saving file', str(e))
def rebuild_map(self, quiet=True): s = self.item.settings try: self.mapdata = bin_mapping(s.xaxis, s.yaxis, self.item.datas, usemask=s.usemask, log=s.logz, yscale=s.yscale, interpolate=s.interp, minmax=(s.zmin, s.zmax)) return True except Exception as err: self.logger.exception('While creating mapping') if not quiet: err_text = str(err) # Qhull errors are *very* verbose. Discard everything except # the first paragraph. err_text = err_text.partition('\n\n')[0] QMessageBox.warning( self, 'Mapping error', 'Could not create mapping: %s (have you ' 'selected the right columns?)' % err_text) return False
def open_data(self, final=False): try: prec = float(self.precisionEdit.text()) except ValueError: QMessageBox.information(self, 'Error', 'Enter a valid precision.') return floatmerge = self.rbFloatMerge.isChecked() xcol = str(self.xcolBox.currentText()) ycol = str(self.ycolBox.currentText()) dycol = str(self.dycolBox.currentText()) mcol = str(self.moncolBox.currentText()) fcol = str(self.filtercolBox.currentText()) if mcol == 'none': mcol = None if dycol == 'sqrt(Y)': dycol = None try: mscale = int(self.monscaleEdit.text()) except Exception: QMessageBox.information(self, 'Error', 'Monitor scale must be integer.') return if fcol == 'none': filter = None else: try: val = float(self.filtervalEdit.text()) except ValueError: val = bytes(self.filtervalEdit.text(), 'utf-8') filter = {fcol: val} dtempl = path_to_str(self.templateEdit.text()) self.loader.template = dtempl numors = str(self.numorsEdit.text()) try: datas = self.loader.load_numors(numors, prec, xcol, ycol, dycol, mcol, mscale, floatmerge, filter) except Exception as e: self.logger.exception('Error while loading data file') QMessageBox.information(self, 'Error', str(e)) return self.last_data = datas if final: self.newDatas.emit(datas, self.groupBox.currentText()) self.closeRequest.emit() else: self.plot()
def export_python(self, filename): QMessageBox.information(self, 'Error', 'Cannot export Python from an ' 'image.')
def export_fits(self, filename): QMessageBox.information(self, 'Error', 'Cannot export fits from an ' 'image.')
def export_python(self, filename): QMessageBox.information(self, 'Error', 'Cannot export Python from a ' 'mapping.')
def export_fits(self, filename): QMessageBox.information(self, 'Error', 'Cannot export fits from a ' 'mapping.')
def on_globalfitBtn_clicked(self): QMessageBox.warning(self, 'Sorry', 'Not implemented yet.')
def main(): app = QApplication([]) app.setOrganizationName('ufit') app.setApplicationName('gui') pixmap = QPixmap(':/splash.png') splash = QSplashScreen(pixmap, Qt.WindowStaysOnTopHint) splash.showMessage(u'Loading...' + u'\xa0' * 10 + '\n\n', Qt.AlignRight | Qt.AlignBottom) splash.show() time.sleep(0.1) with SettingGroup('main') as settings: if settings.value('current_version', '') != __version__: settings.setValue('current_version', __version__) # Execute here the actions to be performed only once after # each update (there is nothing there for now, but it could # be useful some day...) logger.info('Upgrade to version %s finished' % __version__) app.processEvents() def log_unhandled(*exc_info): logger.error('Unhandled exception in Qt callback', exc_info=exc_info) sys.excepthook = log_unhandled t1 = time.time() logger.info('Startup: import finished (%.3f s), starting GUI...' % (t1 - t0)) mainwindow = UFitMain() parser = optparse.OptionParser(usage='''\ Usage: %prog [-b directory] [.ufit file | data file] ''') parser.add_option('-b', '--browse', action='store', metavar='DIR', help='open browse window in specified directory') opts, args = parser.parse_args() if len(args) >= 1: datafile = path.abspath(args[0]) if path.isdir(datafile): # directory given, treat it as -b argument (browse) opts.browse = datafile elif datafile.endswith('.ufit'): try: mainwindow.filename = datafile mainwindow.load_session(datafile) except Exception as err: QMessageBox.warning(mainwindow, 'Error', 'Loading failed: %s' % err) mainwindow.filename = None else: dtempl, numor = extract_template(datafile) mainwindow.dloader.set_template(dtempl, numor, silent=False) mainwindow.show() if opts.browse: mainwindow.dloader.open_browser(opts.browse) t2 = time.time() logger.info('Startup: loading finished (%.3f s), main window opened' % (t2 - t1)) splash.deleteLater() app.exec_()