class MotionOptions(OptionsWidget): def __init__(self, ivm, parent): OptionsWidget.__init__(self, ivm, parent) main_vbox = QtGui.QVBoxLayout() self.setLayout(main_vbox) self.options = OptionBox() self.options.add("Simulate motion", BoolOption(default=False), key="motion") self.options.option("motion").sig_changed.connect( self._update_widget_visibility) self.options.add("Random translation standard deviation (mm)", NumericOption(minval=0, maxval=5, default=1, decimals=2), key="std") self.options.add( "Random rotation standard deviation (\N{DEGREE SIGN})", NumericOption(minval=0, maxval=10, default=1, decimals=2), key="std_rot") self.options.add("Padding (mm)", NumericOption(minval=0, maxval=10, default=5, decimals=1), key="padding", checked=True) self.options.add( "Interpolation", ChoiceOption(["Nearest neighbour", "Linear", "Quadratic", "Cubic"], return_values=range(4), default=3), key="order") main_vbox.addWidget(self.options) main_vbox.addStretch(1) self._update_widget_visibility() def _update_widget_visibility(self): enabled = self.options.option("motion").value for option in ["std", "std_rot", "padding", "order"]: self.options.set_visible(option, enabled)
class AifWidget(QtGui.QWidget): """ Widget allowing choice of AIF """ def __init__(self, ivm): QtGui.QWidget.__init__(self) self.ivm = ivm vbox = QtGui.QVBoxLayout() self.setLayout(vbox) self.optbox = OptionBox() self.optbox.add( "AIF source", ChoiceOption(["Global sequence of values", "Voxelwise image"], ["global", "voxelwise"]), key="aif_source") self.optbox.option("aif_source").sig_changed.connect( self._aif_source_changed) self.optbox.add("AIF", NumberListOption(), key="aif") self.optbox.add("AIF image", DataOption(self.ivm), key="suppdata") self.optbox.add("AIF type", ChoiceOption(["DSC signal", "Concentration"], [False, True]), key="aifconc") vbox.addWidget(self.optbox) vbox.addStretch() self._aif_source_changed() def options(self): """ :return: Dictionary of options selected for the AIF""" opts = self.optbox.values() opts.pop("aif_source") return opts def _aif_source_changed(self): global_aif = self.optbox.option("aif_source").value == "global" self.optbox.set_visible("aif", global_aif) self.optbox.set_visible("suppdata", not global_aif)
class ResampleDataWidget(QpWidget): """ Widget that lets you resample data onto a different grid """ def __init__(self, **kwargs): super(ResampleDataWidget, self).__init__(name="Resample Data", icon="resample.png", desc="Resample data onto a different grid", group="Utilities", **kwargs) def init_ui(self): vbox = QtGui.QVBoxLayout() self.setLayout(vbox) vbox.addWidget(TitleWidget(self)) self.optbox = OptionBox("Resampling options") self.data = self.optbox.add("Data to resample", DataOption(self.ivm), key="data") self.resample_type = self.optbox.add( "Resampling method", ChoiceOption( ["On to grid from another data set", "Upsample", "Downsample"], ["data", "up", "down"]), key="type") self.grid_data = self.optbox.add("Use grid from", DataOption(self.ivm), key="grid") self.factor = self.optbox.add("Factor", NumericOption(default=2, minval=2, maxval=10, intonly=True), key="factor") self.slicewise = self.optbox.add("2D only", BoolOption(), key="2d") self.order = self.optbox.add( "Interpolation", ChoiceOption(["Nearest neighbour", "Linear", "Quadratic", "Cubic"], [0, 1, 2, 3], default=1), key="order") self.output_name = self.optbox.add("Output name", OutputNameOption(src_data=self.data, suffix="_res"), key="output-name") vbox.addWidget(self.optbox) self.resample_type.sig_changed.connect(self._resample_type_changed) self.run = RunButton("Resample", self._run) vbox.addWidget(self.run) vbox.addStretch(1) self._resample_type_changed() def _resample_type_changed(self): resample_type = self.resample_type.value self.optbox.set_visible("grid", resample_type == "data") self.optbox.set_visible("factor", resample_type != "data") self.optbox.set_visible("order", resample_type != "down") self.optbox.set_visible("2d", resample_type != "data") def batch_options(self): options = self.optbox.values() return "Resample", options def _run(self): _, options = self.batch_options() ResampleProcess(self.ivm).run(options)
class RegWidget(QpWidget): """ Generic registration / motion correction widget """ def __init__(self, **kwargs): super(RegWidget, self).__init__(name="Registration", icon="reg", desc="Registration and Motion Correction", group="Registration", **kwargs) self.reg_methods = [] for method in get_plugins("reg-methods"): try: self.reg_methods.append(method(self.ivm)) except: traceback.print_exc() self.warn("Failed to create registration method: %s", method) def init_ui(self): layout = QtGui.QVBoxLayout() self.setLayout(layout) title = TitleWidget(self, title="Registration and Motion Correction", help="reg") layout.addWidget(title) if not self.reg_methods: layout.addWidget(QtGui.QLabel("No registration methods found")) layout.addStretch(1) return self.options = OptionBox("General Options") self.options.add("Mode", ChoiceOption(["Registration", "Motion Correction"], ["reg", "moco"]), key="mode") self.options.add( "Method", ChoiceOption([method.display_name for method in self.reg_methods], self.reg_methods), key="method") self.options.add("Registration data", DataOption(self.ivm), key="reg") self.options.add("Reference data", DataOption(self.ivm), key="ref") self.options.add( "Reference volume", ChoiceOption(["Middle volume", "Mean volume", "Specified volume"], ["median", "mean", "idx"]), key="ref-vol") self.options.add("Reference volume index", NumericOption(intonly=True), key="ref-idx") self.options.add( "Output space", ChoiceOption(["Reference", "Registration", "Transformed"], ["ref", "reg", "trans"]), key="output-space") self.options.add("Output name", OutputNameOption(src_data=self.options.option("reg"), suffix="_reg"), key="output-name", checked=True) self.options.add("Also apply transform to", DataOption(self.ivm, multi=True), key="add-reg") self.options.add("Save transformation", TextOption(), key="save-transform", checked=True, default=False) self.options.option("mode").sig_changed.connect( self._update_option_visibility) self.options.option("method").sig_changed.connect(self._method_changed) self.options.option("ref").sig_changed.connect( self._update_option_visibility) self.options.option("ref-vol").sig_changed.connect( self._update_option_visibility) self.options.option("reg").sig_changed.connect( self._update_option_visibility) layout.addWidget(self.options) # Create the options boxes for reg methods - only one visible at a time! self.opt_boxes = {} for method in self.reg_methods: hbox = QtGui.QHBoxLayout() opt_box = QtGui.QGroupBox() opt_box.setTitle(method.display_name) vbox = QtGui.QVBoxLayout() opt_box.setLayout(vbox) vbox.addWidget(method.interface()) hbox.addWidget(opt_box) opt_box.setVisible(False) layout.addLayout(hbox) self.opt_boxes[method.name] = opt_box layout.addWidget(RunWidget(self)) layout.addStretch(1) self._method_changed() def _method_changed(self): method = self.options.option("method").value for name, box in self.opt_boxes.items(): box.setVisible(name == method.name) self.options.option("save-transform").value = "%s_trans" % method.name self._update_option_visibility() def _update_option_visibility(self): mode = self.options.option("mode").value regdata = self.ivm.data.get(self.options.option("reg").value, None) refdata = self.ivm.data.get(self.options.option("ref").value, None) refvol = self.options.option("ref-vol").value nvols_reg, nvols_ref = 1, 1 if regdata is not None: nvols_reg = regdata.nvols if mode == "moco" and regdata is not None: nvols_ref = regdata.nvols elif mode == "reg" and refdata is not None: nvols_ref = refdata.nvols self.options.set_visible("ref", mode == "reg") self.options.set_visible("ref-vol", nvols_ref > 1) self.options.set_visible("ref-idx", nvols_ref > 1 and refvol == "idx") self.options.set_visible("add-reg", nvols_reg == 1 and mode == "reg") self.options.set_visible("output-space", mode == "reg") if nvols_ref > 1: self.options.option("ref-idx").setLimits(0, nvols_ref - 1) self.options.option("ref-idx").value = int(nvols_ref / 2) def processes(self): options = self.options.values() if options.get("ref-vol", None) == "idx": options["ref-vol"] = options.pop("ref-idx") method = options.pop("method") options["method"] = method.name options.update(method.options()) return { "Reg": options, }
class HistogramWidget(QpWidget): """ Widget which displays data histograms """ def __init__(self, **kwargs): super(HistogramWidget, self).__init__(name="Histogram", icon="hist", desc="Display histograms from data", group="Visualisation", **kwargs) self._updating = False def init_ui(self): vbox = QtGui.QVBoxLayout() self.setLayout(vbox) title = TitleWidget(self) vbox.addWidget(title) self.options = OptionBox("Options") self.options.add("Data", DataOption(self.ivm, multi=True), key="data") self.options.add("Within ROI", DataOption(self.ivm, data=False, rois=True, none_option=True), key="roi") self.options.add("All volumes", BoolOption(default=False), key="allvols") self.options.add("Y-axis scale", ChoiceOption(["Count", "Probability"]), key="yscale") self.options.add("Number of bins", NumericOption(minval=5, maxval=500, default=100, intonly=True), key="bins") self.options.add("Min value", NumericOption(minval=0, maxval=100, default=0), key="min") self.options.add("Max value", NumericOption(minval=0, maxval=500, default=100), key="max") self.options.option("yscale").sig_changed.connect(self._yscale_changed) self.options.option("min").sig_changed.connect(self._min_changed) self.options.option("min").sig_changed.connect(self._max_changed) vbox.addWidget(self.options) self.plot = Plot(qpo=None, parent=self, title="Data histogram", display_mode=False) self.plot.set_xlabel("Data value") self.plot.set_ylabel("Count") vbox.addWidget(self.plot) vbox.addStretch(1) def activate(self): self.ivm.sig_all_data.connect(self._data_changed) self.options.option("data").sig_changed.connect(self._data_changed) self.options.sig_changed.connect(self._update) self._data_changed() def deactivate(self): self.ivm.sig_all_data.disconnect(self._data_changed) self.options.option("data").sig_changed.disconnect(self._data_changed) self.options.sig_changed.disconnect(self._update) def processes(self): opts = self.options.values() if not opts.pop("allvols", False): opts["vol"] = self.ivl.focus()[3] return { "Histogram" : opts } def _yscale_changed(self): self.plot.set_ylabel(self.options.option("yscale").value) def _min_changed(self): minval = self.options.option("min").value self.options.option("max").setLimits(minval=minval) def _max_changed(self): maxval = self.options.option("max").value self.options.option("min").setLimits(maxval=maxval) def _data_changed(self): if self._updating: return self._updating = True try: data_names = self.options.option("data").value vol = None if self.options.option("allvols").value: vol = self.ivl.focus()[3] dmin, dmax, multivol = None, None, False for data_name in data_names: qpdata = self.ivm.data[data_name] multivol = multivol or qpdata.nvols > 1 _dmin, _dmax = qpdata.range(vol=vol) if dmin is None or dmin > _dmin: dmin = _dmin if dmax is None or dmax > dmax: dmax = _dmax if dmin is not None and dmax is not None: self.options.option("min").value = dmin self.options.option("min").setLimits(dmin, dmax) self.options.option("max").value = dmax self.options.option("max").setLimits(dmin, dmax) self.options.set_visible("allvols", multivol) self._update() finally: self._updating = False def _update(self): opts = self.processes()["Histogram"] if opts["data"]: HistogramProcess(self.ivm).run(opts) self.plot.clear() histogram = self.ivm.extras.get("histogram", None) if histogram is not None: for idx, name in enumerate(histogram.col_headers[3:]): xvalues = [row[2] for row in histogram.arr] yvalues = [row[idx+3] for row in histogram.arr] self.plot.add_line(yvalues, name=name, xvalues=xvalues)
class AnalysisOptions(QtGui.QWidget): """ Widget allowing model and output options to be changed """ def __init__(self, ivm=None): QtGui.QWidget.__init__(self) self._ivm = ivm self._poolvals_edited = False vbox = QtGui.QVBoxLayout() self.setLayout(vbox) self.optbox = OptionBox() vbox.addWidget(self.optbox) self.optbox.add("<b>Output options</b>") self.optbox.add("CEST R*", BoolOption(default=True), key="save-model-extras") self.optbox.add("Parameter maps", BoolOption(default=False), key="save-mean") #self.optbox.add("Parameter variance", BoolOption(default=False), key="var") self.optbox.add("Model fit", BoolOption(default=False), key="save-model-fit") self.optbox.add("Prefix for output", TextOption(), checked=True, key="output-prefix") self.optbox.add(" ") self.optbox.add("<b>Analysis options</b>") self.optbox.add("Spatial Regularization", BoolOption(), key="spatial") self.optbox.add("Allow uncertainty in T1/T2 values", BoolOption(), key="t12prior") self.optbox.add("Prior T1 map", DataOption(self._ivm), key="t1img", checked=True) self.optbox.add("Prior T2 map", DataOption(self._ivm), key="t2img", checked=True) self.optbox.add("Tissue PV map (GM+WM)", DataOption(self._ivm), key="pvimg", checked=True) self.optbox.option("t12prior").sig_changed.connect(self._update_ui) self.optbox.add("Use steady state solution for MT bias reduction", BoolOption(default=False), key="new-ss") self.optbox.option("new-ss").sig_changed.connect(self._update_ui) self.optbox.add("TR (s)", NumericOption(default=3.0, minval=0, maxval=5, digits=3, step=0.1), key="tr") self.optbox.add("Excitation flip angle (\N{DEGREE SIGN})", NumericOption(default=12.0, minval=0, maxval=25, digits=3, step=1.0), key="fa") self.optbox.add( "MT pool Line shape", ChoiceOption( ["None", "Gaussian", "Lorentzian", "Super Lorentzian"], ["none", "gaussian", "lorentzian", "superlorentzian"]), key="lineshape") self.alexmt_cite = Citation(ALEXMT_CITE_TITLE, ALEXMT_CITE_AUTHOR, ALEXMT_CITE_JOURNAL) vbox.addWidget(self.alexmt_cite) vbox.addStretch(1) self._update_ui() def _update_ui(self): t12prior = self.optbox.option("t12prior").value self.optbox.set_visible("t1img", t12prior) self.optbox.set_visible("t2img", t12prior) newss = self.optbox.values().get("new-ss", False) self.optbox.set_visible("tr", newss) self.optbox.set_visible("fa", newss) self.optbox.set_visible("lineshape", newss) self.alexmt_cite.setVisible(newss) def set_pools(self, pools): self.optbox.set_visible("new-ss", "MT" in [p.name for p in pools if p.enabled]) self._update_ui() def options(self): options = self.optbox.values() if options.pop("spatial", False): options["method"] = "spatialvb" options["param-spatial-priors"] = "MN+" else: options["method"] = "vb" options.pop("param-spatial-priors", None) # The new MT model is automatically triggered when the TR and FA options are given options.pop("new-ss", None) prior_num = 1 for idx in (1, 2): if "t%iimg" % idx in options: options["PSP_byname%i" % prior_num] = "T%ia" % idx options["PSP_byname%i_type" % prior_num] = "I" options["PSP_byname%i_image" % prior_num] = options.pop( "t%iimg" % idx) prior_num += 1 return options
class SequenceOptions(QtGui.QWidget): """ Widget containing options for the CEST sequence """ sig_b0_changed = QtCore.Signal(float) def __init__(self, ivm=None): QtGui.QWidget.__init__(self) self._ivm = ivm vbox = QtGui.QVBoxLayout() self.setLayout(vbox) self.optbox = OptionBox() vbox.addWidget(self.optbox) self.optbox.add("CEST data", DataOption(self._ivm), key="data") self.optbox.add("ROI", DataOption(self._ivm, rois=True, data=False), key="mask") self.optbox.add("Frequency offsets", NumberListOption(), key="freqs") self.optbox.add("B0", ChoiceOption(B0_DEFAULTS), key="b0") self.optbox.add("Custom B0 (T)", NumericOption(minval=0.0, maxval=15, default=3.0, decimals=3), key="b0_custom") # FIXME multiple B1 values self.optbox.add("B1 (\u03bcT)", NumericOption(minval=0.0, maxval=2, default=0.55, decimals=6), key="b1") self.optbox.add( "Saturation", ChoiceOption(["Continuous Saturation", "Pulsed Saturation"], ["continuous", "pulsed"]), key="sat") self.optbox.add("Saturation time (s)", NumericOption(minval=0.0, maxval=5, default=2, decimals=2), key="sat_time") self.optbox.add("Pulse Magnitudes", NumberListOption(), key="pulse_mag") self.optbox.add("Pulse Durations (s)", NumberListOption(), key="pulse_dur") self.optbox.add("Pulse Repeats", NumberListOption(), key="pulse_repeats") self.optbox.option("b0").sig_changed.connect(self._b0_changed) self.optbox.option("b0_custom").sig_changed.connect(self._b0_changed) self.optbox.option("sat").sig_changed.connect(self._sat_changed) self.warn_box = WarningBox() vbox.addWidget(self.warn_box) # B1 field #hbox = QtGui.QHBoxLayout() #self.unsat_cb = QtGui.QCheckBox("Unsaturated") #self.unsat_cb.stateChanged.connect(self.update_ui) #hbox.addWidget(self.unsat_cb) #self.unsat_combo = QtGui.QComboBox() #self.unsat_combo.addItem("first") #self.unsat_combo.addItem("last") #self.unsat_combo.addItem("first and last ") #hbox.addWidget(self.unsat_combo) #hbox.addStretch(1) #grid.addLayout(hbox, 2, 2) vbox.addStretch(1) self._sat_changed() self._b0_changed() def _sat_changed(self): pulsed = self.optbox.option("sat").value == "pulsed" self.optbox.set_visible("pulse_mag", pulsed) self.optbox.set_visible("pulse_dur", pulsed) self.optbox.set_visible("pulse_repeats", pulsed) def _b0_changed(self): b0_sel = self.optbox.option("b0").value if b0_sel == "Custom": self.optbox.set_visible("b0_custom", True) b0 = self.optbox.option("b0_custom").value else: self.optbox.set_visible("b0_custom", False) b0 = float(b0_sel[:-1]) self.sig_b0_changed.emit(b0) def _get_dataspec(self, options): dataspec = [] freqs = options.pop("freqs") b1 = options.pop("b1") / 1e6 if options["sat"] == "pulsed": repeats = options.pop("pulse_repeats") else: repeats = 1 for idx, freq in enumerate(freqs): #if self.unsat_cb.isChecked(): # self.debug("Unsat", idx, self.unsat_combo.currentIndex()) # if idx == 0 and self.unsat_combo.currentIndex() in (0, 2): # b1 = 0 # elif idx == len(freqs)-1 and self.unsat_combo.currentIndex() in (1, 2): # b1 = 0 dataspec.append([freq, b1, repeats]) #self.debug(dataspec) return dataspec def _get_ptrain(self, options): ptrain = [] if options.pop("sat") == "pulsed": pms = options.pop("pulse_mag") pds = options.pop("pulse_dur") if len(pms) != len(pds): raise QpException( "Pulse magnitude and duration must contain the same number of values" ) for pm, pd in zip(pms, pds): ptrain.append([pm, pd]) else: ptrain.append([1, options.pop("sat_time")]) #self.debug(ptrain) return ptrain def options(self): options = self.optbox.values() options["spec"] = self._get_dataspec(options) options["ptrain"] = self._get_ptrain(options) options.pop("b0") options.pop("b0_custom", None) return options
class DceWidget(QpWidget): """ Widget for DCE Pharmacokinetic modelling """ def __init__(self, **kwargs): super(DceWidget, self).__init__(name="DCE Modelling", desc="DCE kinetic modelling", icon="dce", group="DCE-MRI", **kwargs) def init_ui(self): vbox = QtGui.QVBoxLayout() self.setLayout(vbox) title = TitleWidget(self, help="pk", batch_btn=True, opts_btn=False) vbox.addWidget(title) self.input = OptionBox("Input data") self.input.add("DCE data", DataOption(self.ivm, include_3d=False, include_4d=True), key="data") self.input.add("ROI", DataOption(self.ivm, data=False, rois=True), key="roi") self.input.add("T1 map", DataOption(self.ivm, include_3d=True, include_4d=False), key="t1") vbox.addWidget(self.input) self.options = OptionBox("Options") self.options.add("Contrast agent R1 relaxivity (l/mmol s)", NumericOption(minval=0, maxval=10, default=3.7), key="r1") self.options.add("Contrast agent R2 relaxivity (l/mmol s)", NumericOption(minval=0, maxval=10, default=4.8), key="r2") self.options.add("Flip angle (\N{DEGREE SIGN})", NumericOption(minval=0, maxval=90, default=12), key="fa") self.options.add("TR (ms)", NumericOption(minval=0, maxval=10, default=4.108), key="tr") self.options.add("TE (ms)", NumericOption(minval=0, maxval=10, default=1.832), key="te") self.options.add("Time between volumes (s)", NumericOption(minval=0, maxval=30, default=12), key="dt") self.options.add("Estimated injection time (s)", NumericOption(minval=0, maxval=60, default=30), key="tinj") self.options.add("Ktrans/kep percentile threshold", NumericOption(minval=0, maxval=100, default=100), key="ve-thresh") self.options.add("Dose (mM/kg) - preclinical only", NumericOption(minval=0, maxval=5, default=0.6), key="dose", visible=False) models = [ "Clinical: Toft / OrtonAIF (3rd) with offset", "Clinical: Toft / OrtonAIF (3rd) no offset", "Preclinical: Toft / BiexpAIF (Heilmann)", "Preclinical: Ext Toft / BiexpAIF (Heilmann)", ] self.options.add("Pharmacokinetic model choice", ChoiceOption(models, [1, 2, 3, 4]), key="model") self.options.option("model").sig_changed.connect(self._aif_changed) vbox.addWidget(self.options) # Run button and progress vbox.addWidget(RunWidget(self, title="Run modelling")) vbox.addStretch(1) self._aif_changed() def _aif_changed(self): self.options.set_visible("dose", self.options.option("model").value in (2, 3)) def processes(self): options = self.input.values() options.update(self.options.values()) return {"PkModelling": options}
class FabberDceWidget(QpWidget): """ DCE modelling, using the Fabber process """ def __init__(self, **kwargs): QpWidget.__init__(self, name="Bayesian DCE", icon="dce", group="DCE-MRI", desc="DCE model fitting using Bayesian inference", **kwargs) def init_ui(self): vbox = QtGui.QVBoxLayout() self.setLayout(vbox) try: self.FabberProcess = get_plugins("processes", "FabberProcess")[0] except IndexError: self.FabberProcess = None if self.FabberProcess is None: vbox.addWidget( QtGui.QLabel( "Fabber core library not found.\n\n You must install Fabber to use this widget" )) return title = TitleWidget( self, help="fabber-dsc", subtitle="DSC modelling using the Fabber process %s" % __version__) vbox.addWidget(title) cite = Citation(FAB_CITE_TITLE, FAB_CITE_AUTHOR, FAB_CITE_JOURNAL) vbox.addWidget(cite) self.input = OptionBox("Input data") self.input.add("DCE data", DataOption(self.ivm, include_3d=False, include_4d=True), key="data") self.input.add("ROI", DataOption(self.ivm, data=False, rois=True), key="roi", checked=True) self.input.add("T1 map", DataOption(self.ivm, include_3d=True, include_4d=False), key="t1", checked=True) self.input.option("t1").sig_changed.connect(self._t1_map_changed) vbox.addWidget(self.input) self.acquisition = OptionBox("Acquisition") self.acquisition.add("Contrast agent R1 relaxivity (l/mmol s)", NumericOption(minval=0, maxval=10, default=3.7), key="r1") self.acquisition.add("Flip angle (\N{DEGREE SIGN})", NumericOption(minval=0, maxval=90, default=12), key="fa") self.acquisition.add("TR (ms)", NumericOption(minval=0, maxval=10, default=4.108), key="tr") self.acquisition.add("Time between volumes (s)", NumericOption(minval=0, maxval=30, default=12), key="delt") vbox.addWidget(self.acquisition) self.model = OptionBox("Model options") self.model.add( "Model", ChoiceOption([ "Standard Tofts model", "Extended Tofts model (ETM)", "2 Compartment exchange model", "Compartmental Tissue Update (CTU) model", "Adiabatic Approximation to Tissue Homogeneity (AATH) Model" ], ["dce_tofts", "dce_ETM", "dce_2CXM", "dce_CTU", "dce_AATH"]), key="model") self.model.add( "AIF", ChoiceOption([ "Population (Orton 2008)", "Population (Parker)", "Measured DCE signal", "Measured concentration curve" ], ["orton", "parker", "signal", "conc"]), key="aif") self.model.add("Bolus injection time (s)", NumericOption(minval=0, maxval=60, default=30), key="tinj") self.model.add("AIF data values", NumberListOption([ 0, ]), key="aif-data") self.model.add("T1 (s)", NumericOption(minval=0.0, maxval=5.0, default=1.0), key="t10") self.model.add("Allow T1 to vary", BoolOption(default=False), key="infer-t10") self.model.add("Bolus arrival time (s)", NumericOption(minval=0, maxval=2.0, default=0), key="delay") self.model.add("Allow bolus arrival time to vary", BoolOption(default=False), key="infer-delay") self.model.add("Infer kep rather than ve", BoolOption(default=False), key="infer-kep") self.model.add("Infer flow", BoolOption(default=True), key="infer-fp") self.model.add("Infer permeability-surface area", BoolOption(default=False), key="infer-ps") self.model.add("Spatial regularization", BoolOption(default=False), key="spatial") self.model.option("model").sig_changed.connect(self._model_changed) self.model.option("aif").sig_changed.connect(self._aif_changed) vbox.addWidget(self.model) # Run button and progress vbox.addWidget(RunWidget(self, title="Run modelling")) vbox.addStretch(1) self._aif_changed() self._model_changed() def _t1_map_changed(self): self.model.set_visible("t10", "t1" not in self.input.values()) def _aif_changed(self): aif_source = self.model.option("aif").value self.model.set_visible("tinj", aif_source not in ("signal", "conc")) self.model.set_visible("aif-data", aif_source in ("signal", "conc")) def _model_changed(self): self.model.set_visible( "infer-kep", self.model.option("model").value in ("dce_tofts", "dce_ETM")) self.model.set_visible("infer-fp", self.model.option("model").value == "dce_AATH") self.model.set_visible("infer-ps", self.model.option("model").value == "dce_AATH") def processes(self): options = { "model-group": "dce", "method": "vb", "noise": "white", "save-mean": True, "save-model-fit": True, "convergence": "trialmode", "max-trials": 20, "max-iterations": 50, "infer-sig0": True, } options.update(self.input.values()) options.update(self.acquisition.values()) options.update(self.model.values()) # Extended Tofts model is the same model name but with inference of Vp if options["model"] == "dce_ETM": options["model"] = "dce_tofts" options["infer-vp"] = True # T1 map is an image prior if "t1" in options: options.update({ "PSP_byname1": "t10", "PSP_byname1_type": "I", "PSP_byname1_image": options.pop("t1") }) if not options["infer-t10"]: # To treat the image prior as ground truth need to put T10 # into the model but give the image prior a high precision so # the parameter doesn't actually have any freedom to vary options["infer-t10"] = True options["PSP_byname1_prec"] = 1e6 # Delay time to include injection time for population AIF if "tinj" in options: options["delay"] = options["delay"] + options.pop("tinj") # Times in minutes and TR in s options["delt"] = options["delt"] / 60 options["delay"] = options["delay"] / 60 options["tr"] = options["tr"] / 1000 # Spatial mode if options.pop("spatial", False): options["method"] = "spatialvb" options["param-spatial-priors"] = "M+" return {"Fabber": options}
class AifWidget(QpWidget): """ Widget which allows the user to define an arterial input function from signal data """ def __init__(self, **kwargs): super(AifWidget, self).__init__(name="Arterial Input Function", icon="aif", desc="Tool for defining an AIF from measured data", group="Utilities", **kwargs) self._aif = [] self._aif_points = [] def init_ui(self): vbox = QtGui.QVBoxLayout() self.setLayout(vbox) title = TitleWidget(self) vbox.addWidget(title) self._options = OptionBox("Options") self._options.add("Signal data", DataOption(self.ivm), key="data") self._clear_btn = QtGui.QPushButton("Clear points") self._options.add("Method", ChoiceOption(["Pick points", "Use existing ROI"], ["points", "roi"]), self._clear_btn, key="method") self._options.add("ROI", DataOption(self.ivm, data=False, rois=True), key="roi") self._view_btn = QtGui.QPushButton("View") self._view_btn.setEnabled(False) self._save_btn = QtGui.QPushButton("Save") self._save_btn.setEnabled(False) self._options.add("AIF name", TextOption("aif"), self._view_btn, self._save_btn, key="output-name") self._options.option("method").sig_changed.connect(self._method_changed) self._options.option("data").sig_changed.connect(self._recalc_aif) self._options.option("roi").sig_changed.connect(self._recalc_aif) self._clear_btn.clicked.connect(self._clear_btn_clicked) self._save_btn.clicked.connect(self._save_btn_clicked) self._view_btn.clicked.connect(self._view_btn_clicked) vbox.addWidget(self._options) self._plot = Plot(qpo=None, parent=self, title="AIF", display_mode=False) self._plot.set_xlabel("Volume") self._plot.set_ylabel("Signal") vbox.addWidget(self._plot) vbox.addStretch(1) def activate(self): self.ivl.sig_selection_changed.connect(self._selection_changed) self._method_changed() def deactivate(self): self.ivl.sig_selection_changed.disconnect(self._selection_changed) self.ivl.set_picker(PickMode.SINGLE) @property def qpdata(self): return self.ivm.data.get(self._options.option("data").value, None) @property def roi(self): return self.ivm.data.get(self._options.option("roi").value, None) @property def method(self): return self._options.option("method").value def _method_changed(self): self._options.set_visible("roi", self.method == "roi") self._clear_btn.setVisible(self.method == "points") if self.method == "roi": self.ivl.set_picker(PickMode.SINGLE) else: self.ivl.set_picker(PickMode.MULTIPLE) self._selection_changed() self._recalc_aif() def _clear_btn_clicked(self): if self.method == "points": self.ivl.set_picker(PickMode.MULTIPLE) self._selection_changed() # FIXME should be signalled by picker def _save_btn_clicked(self): name = self._options.option("output-name").value extra = NumberListExtra(name, self._aif) self.ivm.add_extra(name, extra) self._save_btn.setEnabled(False) def _view_btn_clicked(self): aiftxt = ", ".join([str(v) for v in self._aif]) TextViewerDialog(self, title="AIF data", text=aiftxt).exec_() def _selection_changed(self): if self.method == "roi": return self._aif_points = [] for _col, points in self.ivl.picker.selection().items(): self._aif_points += list(points) self._recalc_aif() def _recalc_aif(self): self._aif = [] self._save_btn.setEnabled(True) self._view_btn.setEnabled(True) if self.qpdata is not None: if self.method == "roi": self._calc_aif_roi() else: self._calc_aif_points() self._update_plot() def _calc_aif_roi(self): if self.roi is None: return points = self.qpdata.raw()[self.roi.raw() > 0] if len(points) > 0: aif = None for sig in points: if aif is None: aif = np.zeros([len(sig)], dtype=np.float32) aif += sig self._aif = aif / len(points) def _calc_aif_points(self): aif = None num_points = 0 for point in self._aif_points: sig = self.qpdata.timeseries(point, grid=self.ivl.grid) self.debug("AIF signal: %s", sig) if aif is None: aif = np.zeros([len(sig)], dtype=np.float32) aif += sig num_points += 1 if num_points > 0: self._aif = aif / num_points def _update_plot(self): self._plot.clear() self._plot.add_line(self._aif, name="AIF")
class DceDataModelView: def __init__(self, ivm): self.model = DceDataModel(ivm) self.gui = OptionBox() self.gui.add( "Model", ChoiceOption([ "Standard Tofts model", "Extended Tofts model (ETM)", "2 Compartment exchange model", "Compartmental Tissue Update (CTU) model", "Adiabatic Approximation to Tissue Homogeneity (AATH) Model" ], ["dce_tofts", "dce_ETM", "dce_2CXM", "dce_CTU", "dce_AATH"]), key="model") self.gui.add("Contrast agent R1 relaxivity (l/mmol s)", NumericOption(minval=0, maxval=10, default=3.7), key="r1") self.gui.add("Flip angle (\N{DEGREE SIGN})", NumericOption(minval=0, maxval=90, default=12), key="fa") self.gui.add("TR (ms)", NumericOption(minval=0, maxval=10, default=4.108), key="tr") self.gui.add("Time between volumes (s)", NumericOption(minval=0, maxval=30, default=12), key="delt") self.gui.add("AIF", ChoiceOption([ "Population (Orton 2008)", "Population (Parker)", "Measured DCE signal", "Measured concentration curve" ], ["orton", "parker", "signal", "conc"]), key="aif") self.gui.add("Number of volumes", NumericOption(minval=0, maxval=100, default=20, intonly=True), key="nt") self.gui.add("Bolus injection time (s)", NumericOption(minval=0, maxval=60, default=30), key="tinj") self.gui.add("AIF data values", NumberListOption([ 0, ]), key="aif-data") self.gui.add("Arterial transit time (s)", NumericOption(minval=0, maxval=1.0, default=0), key="delay") self.gui.option("model").sig_changed.connect(self._model_changed) self.gui.option("aif").sig_changed.connect(self._aif_changed) self._aif_changed() self._model_changed() self.gui.sig_changed.connect(self._update_options) self._update_options() def _aif_changed(self): aif_source = self.gui.option("aif").value self.gui.set_visible("tinj", aif_source not in ("signal", "conc")) self.gui.set_visible("aif-data", aif_source in ("signal", "conc")) self.gui.set_visible("nt", aif_source not in ("signal", "conc")) def _model_changed(self): pass def _update_options(self): self.model.options.update(self.gui.values())
class AcquisitionOptions(OptionsWidget): N_REGRESSORS = 3 def __init__(self, ivm, parent): OptionsWidget.__init__(self, ivm, parent) vbox = QtWidgets.QVBoxLayout() self.setLayout(vbox) self._optbox = OptionBox() self._optbox.add("<b>Data</b>") self._optbox.add("BOLD timeseries data", DataOption(self.ivm), key="data") self._optbox.add("ROI", DataOption(self.ivm, rois=True, data=False), key="roi") #self._optbox.add("Physiological data (CO<sub>2</sub>/O<sub>2</sub>)", FileOption(plot_btn=True), key="phys-data") #self._optbox.add("Sampling frequency (Hz)", NumericOption(minval=0, maxval=1000, default=100, intonly=True), key="samp-rate") self._optbox.add("TR for MRI timeseries (s)", NumericOption(minval=0, maxval=5, default=1.0), key="tr") self._optbox.add("Baseline period (s)", NumericOption(minval=0, maxval=200, default=60, intonly=True), key="baseline") self._optbox.add("MRI timeseries alignment", ChoiceOption(["Automatic", "Manual"]), key="mri-align") self._optbox.option("mri-align").sig_changed.connect( self._align_changed) self._optbox.add("MRI timeseries start time (s)", NumericOption(minval=0, maxval=1000, default=0), key="data-start-time") vbox.addWidget(self._optbox) self._optbox_reg = OptionBox() self._optbox_reg.add("<b>Regressors</b>") for idx in range(self.N_REGRESSORS): self._optbox_reg.add("Regressor %i" % (idx + 1), ChoiceOption([ "Unprocessed CO2", "Preprocessed pETCO2", "Ramp (linear drift)", "Custom" ], ["co2", "petco2", "ramp", "custom"]), checked=True, default=True, key="type_%i" % (idx + 1)) self._optbox_reg.option("type_%i" % (idx + 1)).sig_changed.connect( self._regressor_changed) self._optbox_reg.add("Data (CO<sub>2</sub>/O<sub>2</sub>)", FileOption(plot_btn=True), key="data_%i" % (idx + 1)) self._optbox_reg.add("Time resolution (s)", NumericOption(minval=0, maxval=10, default=1), key="tr_%i" % (idx + 1)) vbox.addWidget(self._optbox_reg) vbox.addStretch(1) self._regressor_changed() self._align_changed() def _regressor_changed(self): for idx in range(self.N_REGRESSORS): opts = self._optbox_reg.values() extras_visible = "type_%i" % ( idx + 1) in opts and opts["type_%i" % (idx + 1)] != "ramp" self._optbox_reg.set_visible("data_%i" % (idx + 1), extras_visible) self._optbox_reg.set_visible("tr_%i" % (idx + 1), extras_visible) def _add_regressor_options(self, opts): regressors = [] regressor_types = [] regressor_trs = [] reg_opts = self._optbox_reg.values() for idx in range(self.N_REGRESSORS): regressor_type = reg_opts.get("type_%i" % (idx + 1), None) if regressor_type is not None: if regressor_type != "ramp": regressor_types.append(regressor_type) regressors.append(reg_opts["data_%i" % (idx + 1)]) regressor_trs.append(reg_opts["tr_%i" % (idx + 1)]) else: # FIXME can't mix file regressors with Numpy array, need to write to tmp file regressor_types.append("custom") regressor_trs.append(opts["tr"]) regressors.append( np.linspace(0, 1, self.ivm.data[opts["data"]].nvols)) opts["regressors"] = ",".join(regressors) opts["regressor_trs"] = ",".join(["%.3f" % v for v in regressor_trs]) opts["regressor_types"] = ",".join(regressor_types) def _align_changed(self): self._optbox.set_visible( "data-start-time", self._optbox.option("mri-align").value == "Manual") def options(self): opts = self._optbox.values() opts.pop("mri-align", None) self._add_regressor_options(opts) return opts
class AtlasDescription(QtGui.QWidget): """ Displays atlas description """ sig_selected = QtCore.Signal(object) def __init__(self, parent, registry): super(AtlasDescription, self).__init__(parent) self._registry = registry self.ivm = parent.ivm self._desc = None grid = QtGui.QGridLayout() self.setLayout(grid) grid.addWidget(QtGui.QLabel("Name"), 0, 0) self._name = QtGui.QLabel() grid.addWidget(self._name, 0, 1) grid.addWidget(QtGui.QLabel("Type"), 1, 0) self._type = QtGui.QLabel() grid.addWidget(self._type, 1, 1) grid.addWidget(QtGui.QLabel("Resolutions"), 2, 0) self._imgs = QtGui.QComboBox() grid.addWidget(self._imgs, 2, 1) self._label_table = QtGui.QTableView() self._label_model = QtGui.QStandardItemModel() self._label_table.setModel(self._label_model) self._label_table.setSelectionBehavior( QtGui.QAbstractItemView.SelectRows) self._label_table.setSelectionMode( QtGui.QAbstractItemView.SingleSelection) self._label_table.setEditTriggers( QtGui.QAbstractItemView.NoEditTriggers) self._label_table.selectionModel().selectionChanged.connect( self._region_changed) self._label_table.setStyleSheet( "font-size: 10px; alternate-background-color: #6c6c6c;") self._label_table.setShowGrid(False) self._label_table.setTextElideMode(QtCore.Qt.ElideLeft) self._label_table.setAlternatingRowColors(True) self._label_table.ensurePolished() fm = QtGui.QFontMetrics(self._label_table.font()) self._label_table.verticalHeader().setVisible(False) self._label_table.verticalHeader().setSectionResizeMode( QtGui.QHeaderView.Fixed) self._label_table.verticalHeader().setDefaultSectionSize(fm.height() + 2) grid.addWidget(self._label_table, 3, 0, 1, 2) grid.setRowStretch(3, 1) self._load_options = OptionBox() self._load_options.add("Regions", ChoiceOption(["Selected region", "All regions"], ["sel", "all"]), key="regions") self._load_options.add( "Load as", ChoiceOption(["New dataset", "Add to existing dataset"], ["new", "add"]), key="add") self._load_options.add("Dataset name", TextOption("atlas"), key="name") self._load_options.add("Existing dataset", DataOption(self.ivm), key="data") self._load_options.option("regions").sig_changed.connect( self._load_regions_changed) self._load_options.option("add").sig_changed.connect(self._add_changed) grid.addWidget(self._load_options, 4, 0, 1, 2) hbox = QtGui.QHBoxLayout() btn = QtGui.QPushButton("Load") btn.clicked.connect(self._load) hbox.addWidget(btn) hbox.addStretch(1) grid.addLayout(hbox, 5, 0, 1, 2) self._add_changed() def _name_to_dataset_name(self, name): return name.replace(" ", "_").replace("-", "_").replace(",", "").replace( "(", "").replace(")", "").lower() def set_atlas(self, atlas_desc): self._desc = atlas_desc self._name.setText(atlas_desc.name) self._type.setText(atlas_desc.atlasType) self._load_options.option("name").value = self._name_to_dataset_name( atlas_desc.name) self._imgs.clear() for pixdim in atlas_desc.pixdims: pixdim_str = "%.2g mm x %.2g mm x %.2g mm" % pixdim self._imgs.addItem(pixdim_str, pixdim[0]) self._label_model.clear() self._label_model.setColumnCount(2) self._label_model.setHorizontalHeaderLabels(["Index", "Name"]) for label in atlas_desc.labels: index_item = QtGui.QStandardItem("%i" % label.index) name_item = QtGui.QStandardItem(label.name) self._label_model.appendRow([index_item, name_item]) self._label_table.horizontalHeader().setResizeMode( 0, QtGui.QHeaderView.ResizeToContents) self._label_table.horizontalHeader().setResizeMode( 1, QtGui.QHeaderView.Stretch) self._load_options.option("regions").value = "all" def _load_regions_changed(self): if self._load_options.values()["regions"] == "sel": self._region_changed() else: self._load_options.option( "name").value = self._name_to_dataset_name(self._desc.name) def _region_changed(self): if self._load_options.values()["regions"] == "sel": indexes = self._label_table.selectionModel().selectedRows() if indexes: region_name = self._label_model.item(indexes[0].row(), 1).text() if region_name: self._load_options.option( "name").value = self._name_to_dataset_name(region_name) def _add_changed(self): add = self._load_options.values()["add"] == "add" self._load_options.set_visible("name", not add) self._load_options.set_visible("data", add) def _load(self): if self._desc is not None: res = self._imgs.itemData(self._imgs.currentIndex()) atlas = self._registry.loadAtlas(self._desc.atlasID, loadSummary=False, resolution=res) is_roi = self._desc.atlasType == "label" new_name = self._load_options.option("name").value add_name = self._load_options.option("data").value add = self._load_options.option("add").value == "add" load_all = self._load_options.option("regions").value == "all" vol = None if not load_all: indexes = self._label_table.selectionModel().selectedRows() vol = int(self._label_model.item(indexes[0].row(), 0).text()) new_data = fslimage_to_qpdata(atlas, vol=vol, name=new_name, roi=is_roi) if add and add_name in self.ivm.data: # User wants to add the region to an existing data set if load_all: raise QpException( "Cannot add data to existing data set when loading all regions" ) orig_data = self.ivm.data[add_name] if not orig_data.grid.matches(new_data.grid): raise QpException( "Can't add data to existing data set - grids do not match" ) if is_roi and not orig_data.roi: raise QpException( "Can't add data to existing data set - it is not an ROI" ) new_data = NumpyData(orig_data.raw() + new_data.raw(), grid=new_data.grid, name=add_name, roi=is_roi) self.ivm.add(new_data, make_current=True)
class RadialProfileWidget(QpWidget): """ Widget which displays radial profile of data """ def __init__(self, **kwargs): super(RadialProfileWidget, self).__init__(name="Radial profile", icon="rp", desc="Display radial profile of data", group="Visualisation", **kwargs) self._updating = False def init_ui(self): vbox = QtGui.QVBoxLayout() self.setLayout(vbox) title = TitleWidget(self) vbox.addWidget(title) self.options = OptionBox("Options") self.options.add("Data", DataOption(self.ivm, multi=True), key="data") self.options.add("Within ROI", DataOption(self.ivm, data=False, rois=True, none_option=True), key="roi") self.options.add("All volumes", BoolOption(default=False), key="allvols") self.options.add("Number of bins", NumericOption(minval=5, maxval=250, default=50, intonly=True), key="bins") vbox.addWidget(self.options) self.plot = Plot(qpo=None, parent=self, title="Radial profile", display_mode=False) self.plot.set_xlabel("Distance (mm)") self.plot.set_ylabel("Mean data value") vbox.addWidget(self.plot) vbox.addStretch(1) def activate(self): self.ivm.sig_all_data.connect(self._data_changed) self.ivl.sig_focus_changed.connect(self._update) self.options.option("data").sig_changed.connect(self._data_changed) self.options.sig_changed.connect(self._update) self._data_changed() def deactivate(self): self.ivm.sig_all_data.disconnect(self._data_changed) self.ivl.sig_focus_changed.disconnect(self._update) self.options.option("data").sig_changed.disconnect(self._data_changed) self.options.sig_changed.disconnect(self._update) def processes(self): opts = self.options.values() opts["centre"] = self.ivl.focus() if opts.pop("allvols", False): opts["centre"] = opts["centre"][:3] return {"RadialProfile": opts} def _data_changed(self): if self._updating: return self._updating = True try: data_names = self.options.option("data").value multivol = False for data_name in data_names: multivol = multivol or self.ivm.data[data_name].nvols > 1 self.options.set_visible("allvols", multivol) self._update() finally: self._updating = False def _update(self): process = RadialProfileProcess(self.ivm) process.execute(self.processes()["RadialProfile"]) self._update_plot() def _update_plot(self): self.plot.clear() rp = self.ivm.extras.get("radial-profile", None) if rp is not None: xvalues = rp.df.index for idx, name in enumerate(rp.df.columns): yvalues = rp.df.values[:, idx] self.plot.add_line(yvalues, name=name, xvalues=xvalues)
class AddEmbeddingDialog(QtGui.QDialog): """ Dialog box enabling one item to be chosen from a list """ def __init__(self, parent, ivm, existing_strucs): super(AddEmbeddingDialog, self).__init__(parent) self.ivm = ivm self.sel_text = None self.sel_data = None self.existing_names = [struc.name for struc in existing_strucs] self.setWindowTitle("Add embedding") vbox = QtGui.QVBoxLayout() self.setLayout(vbox) self._opts = OptionBox() pvmap = self._opts.add("PV map / mask", DataOption(ivm, data=True, rois=True), key="pvmap") pvmap.sig_changed.connect(self._pvmap_changed) self._opts.add("ROI region", ChoiceOption([]), key="region") name = self._opts.add("Name of embedded structure", TextOption(), key="name") name.textChanged.connect(self._name_changed) self._opts.add("Structure type", ChoiceOption(["Embedding", "Activation mask", "Additional PVE"], return_values=["embed", "act", "add"]), key="type") self._opts.add("Parent structure", ChoiceOption([s.display_name for s in existing_strucs], [s.name for s in existing_strucs]), key="parent") self._opts.add("Edge smoothing sigma (mm)", NumericOption(minval=0, maxval=10, default=1, decimals=2), checked=True, enabled=False, key="sigma") vbox.addWidget(self._opts) self._warning = QtWidgets.QLabel(parent=self) self._warning.setStyleSheet("color: red;") vbox.addWidget(self._warning) self.button_box = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel) self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.reject) self.button_box.button(QtGui.QDialogButtonBox.Ok).setEnabled(False) vbox.addWidget(self.button_box) self._pvmap_changed() def _pvmap_changed(self): if self.pvmap: qpdata = self.ivm.data[self.pvmap] if not self.name: self._opts.option("name").value = qpdata.name if qpdata.roi: regions = list(qpdata.regions.keys()) region_names = list(qpdata.regions.values()) self._opts.option("region").setChoices(region_names, regions) self._opts.set_visible("region", qpdata.roi and len(qpdata.regions) > 1) self._opts.set_visible("sigma", qpdata.roi) def _name_changed(self): accept = self.name != "" and self.name not in self.existing_names self.button_box.button(QtGui.QDialogButtonBox.Ok).setEnabled(accept) if self.name and not accept: self._warning.setText("Name already exists") else: self._warning.setText("") @property def pvmap(self): return self._opts.option("pvmap").value @property def region(self): return self._opts.values().get("region", None) @property def sigma(self): return self._opts.values().get("sigma", None) @property def name(self): return self._opts.option("name").value @property def struc_type(self): return self._opts.option("type").value @property def parent_struc(self): return self._opts.option("parent").value
class UserPvModelView: """ View for UserPvModel - a structural model where user supplies partial volume maps """ def __init__(self, ivm): self.ivm = ivm self.model = UserPvModel(ivm) self.model.options.update({ "pvmaps" : {}, "additional" : {}, }) self.gui = OptionBox() self.gui.sig_changed.connect(self._update_options) self._refresh_gui() def _refresh_gui(self): options = dict(self.model.options) self.gui.clear() for struc in self.model.default_strucs: data_opt = self.gui.add("%s map" % struc.name.upper(), DataOption(self.model._ivm, explicit=True), checked=True, enabled=struc.name in options["pvmaps"], key=struc.name) data_opt.value = options["pvmaps"].get(struc.name, None) for struc in options["additional"].values(): del_btn = self._struc_delete_btn(struc) display_type = {"add" : "map", "embed" : "embedding", "act" : "mask"}.get(struc["struc_type"], "map") data_opt = self.gui.add("%s %s" % (struc["name"], display_type), DataOption(self.model._ivm, explicit=True, rois=True), del_btn, key=struc["name"]) data_opt.value = struc.get("pvmap", None) res_opts = options.get("resampling", {}) self.gui.add("Resampling", ChoiceOption(["Downsample", "From another data set", "Specified resolution"], ["down", "data", "res"], default=res_opts.get("type", "res")), checked=True, enabled="type" in res_opts, key="type") self.gui.add("Output space from", DataOption(self.ivm), key="grid") self.gui.add("Output resample factor", NumericOption(intonly=True, minval=1, maxval=10, default=2), key="factor") self.gui.add("Voxel sizes (mm)", NumberListOption(load_btn=False), key="voxel-sizes") for opt in ("grid", "factor", "voxel-sizes"): if opt in res_opts: self.gui.option(opt).value = res_opts[opt] self.gui.add(None, RunButton("Add user-defined structure", callback=self._add_embedding), key="add_embedding") self._update_resamp_visibility() def _update_resamp_visibility(self): resample_type = self.gui.values().pop("type", "") self.gui.set_visible("grid", resample_type == "data") self.gui.set_visible("factor", resample_type in ("up", "down")) #self.gui.set_visible("order", resample_type != "down") #self.gui.set_visible("2d", resample_type in ("up", "down")) self.gui.set_visible("voxel-sizes", resample_type == "res") def _update_options(self): self._update_resamp_visibility() opts = self.gui.values() pvmaps = {} for struc in self.model.default_strucs: if struc.name in opts: pvmaps[struc.name] = opts[struc.name] self.model.options["pvmaps"] = pvmaps resamp = {} for resamp_opt in ("type", "grid", "factor", "voxel-sizes"): if resamp_opt in opts: resamp[resamp_opt] = opts[resamp_opt] self.model.options["resampling"] = resamp def _struc_delete_btn(self, add_struc): def _del_cb(): self._del_struc(add_struc["name"]) btn = QtGui.QPushButton("Delete") btn.clicked.connect(_del_cb) return btn def _del_struc(self, name): self.model.options["additional"].pop(name, None) self._refresh_gui() def _add_embedding(self): dialog = AddEmbeddingDialog(self.gui, self.model._ivm, self.model.default_strucs) try: accept = dialog.exec_() except: import traceback traceback.print_exc() if accept: self.model.options["additional"][dialog.name] = { "name" : dialog.name, "struc_type" : dialog.struc_type, "parent_struc" : dialog.parent_struc, "pvmap" : dialog.pvmap, } if dialog.region: self.model.options["additional"][dialog.name]["region"] = dialog.region if dialog.sigma: self.model.options["additional"][dialog.name]["sigma"] = dialog.sigma self._refresh_gui()