class OWHyper(OWWidget): name = "HyperSpectra" class Inputs: data = Input("Data", Orange.data.Table, default=True) class Outputs: selected_data = Output("Selection", Orange.data.Table, default=True) annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Orange.data.Table) icon = "icons/hyper.svg" priority = 20 replaces = ["orangecontrib.infrared.widgets.owhyper.OWHyper"] keywords = ["image", "spectral", "chemical", "imaging"] settings_version = 5 settingsHandler = DomainContextHandler() imageplot = SettingProvider(ImagePlot) curveplot = SettingProvider(CurvePlotHyper) integration_method = Setting(0) integration_methods = Integrate.INTEGRALS value_type = Setting(0) attr_value = ContextSetting(None) show_visible_image = Setting(False) visible_image_name = Setting(None) visible_image_composition = Setting('Normal') visible_image_opacity = Setting(120) lowlim = Setting(None) highlim = Setting(None) choose = Setting(None) graph_name = "imageplot.plotview" # defined so that the save button is shown class Warning(OWWidget.Warning): threshold_error = Msg("Low slider should be less than High") class Error(OWWidget.Error): image_too_big = Msg("Image for chosen features is too big ({} x {}).") class Information(OWWidget.Information): not_shown = Msg("Undefined positions: {} data point(s) are not shown.") @classmethod def migrate_settings(cls, settings_, version): if version < 2: # delete the saved attr_value to prevent crashes try: del settings_["context_settings"][0].values["attr_value"] except: # pylint: disable=bare-except pass # migrate selection if version <= 2: try: current_context = settings_["context_settings"][0] selection = getattr(current_context, "selection", None) if selection is not None: selection = [(i, 1) for i in np.flatnonzero(np.array(selection))] settings_.setdefault( "imageplot", {})["selection_group_saved"] = selection except: # pylint: disable=bare-except pass @classmethod def migrate_context(cls, context, version): if version <= 3 and "curveplot" in context.values: CurvePlot.migrate_context_sub_feature_color( context.values["curveplot"], version) def __init__(self): super().__init__() dbox = gui.widgetBox(self.controlArea, "Image values") rbox = gui.radioButtons(dbox, self, "value_type", callback=self._change_integration) gui.appendRadioButton(rbox, "From spectra") self.box_values_spectra = gui.indentedBox(rbox) gui.comboBox(self.box_values_spectra, self, "integration_method", items=(a.name for a in self.integration_methods), callback=self._change_integral_type) gui.rubber(self.controlArea) gui.appendRadioButton(rbox, "Use feature") self.box_values_feature = gui.indentedBox(rbox) self.feature_value_model = DomainModel( DomainModel.SEPARATED, valid_types=DomainModel.PRIMITIVE) self.feature_value = gui.comboBox(self.box_values_feature, self, "attr_value", contentsLength=12, searchable=True, callback=self.update_feature_value, model=self.feature_value_model) splitter = QSplitter(self) splitter.setOrientation(Qt.Vertical) self.imageplot = ImagePlot(self) self.imageplot.selection_changed.connect(self.output_image_selection) # do not save visible image (a complex structure as a setting; # only save its name) self.visible_image = None self.setup_visible_image_controls() self.curveplot = CurvePlotHyper(self, select=SELECTONE) self.curveplot.selection_changed.connect(self.redraw_integral_info) self.curveplot.plot.vb.x_padding = 0.005 # pad view so that lines are not hidden splitter.addWidget(self.imageplot) splitter.addWidget(self.curveplot) self.mainArea.layout().addWidget(splitter) self.line1 = MovableVline(position=self.lowlim, label="", report=self.curveplot) self.line1.sigMoved.connect(lambda v: setattr(self, "lowlim", v)) self.line2 = MovableVline(position=self.highlim, label="", report=self.curveplot) self.line2.sigMoved.connect(lambda v: setattr(self, "highlim", v)) self.line3 = MovableVline(position=self.choose, label="", report=self.curveplot) self.line3.sigMoved.connect(lambda v: setattr(self, "choose", v)) for line in [self.line1, self.line2, self.line3]: line.sigMoveFinished.connect(self.changed_integral_range) self.curveplot.add_marking(line) line.hide() self.markings_integral = [] self.data = None self.disable_integral_range = False self.resize(900, 700) self._update_integration_type() # prepare interface according to the new context self.contextAboutToBeOpened.connect( lambda x: self.init_interface_data(x[0])) def setup_visible_image_controls(self): self.visbox = gui.widgetBox(self.controlArea, True) gui.checkBox(self.visbox, self, 'show_visible_image', label='Show visible image', callback=lambda: (self.update_visible_image_interface(), self.update_visible_image())) self.visible_image_model = VisibleImageListModel() gui.comboBox(self.visbox, self, 'visible_image', model=self.visible_image_model, callback=self.update_visible_image) self.visual_image_composition_modes = OrderedDict([ ('Normal', QPainter.CompositionMode_Source), ('Overlay', QPainter.CompositionMode_Overlay), ('Multiply', QPainter.CompositionMode_Multiply), ('Difference', QPainter.CompositionMode_Difference) ]) gui.comboBox(self.visbox, self, 'visible_image_composition', label='Composition mode:', model=PyListModel( self.visual_image_composition_modes.keys()), callback=self.update_visible_image_composition_mode) gui.hSlider(self.visbox, self, 'visible_image_opacity', label='Opacity:', minValue=0, maxValue=255, step=10, createLabel=False, callback=self.update_visible_image_opacity) self.update_visible_image_interface() self.update_visible_image_composition_mode() self.update_visible_image_opacity() def update_visible_image_interface(self): controlled = [ 'visible_image', 'visible_image_composition', 'visible_image_opacity' ] for c in controlled: getattr(self.controls, c).setEnabled(self.show_visible_image) def update_visible_image_composition_mode(self): self.imageplot.set_visible_image_comp_mode( self.visual_image_composition_modes[ self.visible_image_composition]) def update_visible_image_opacity(self): self.imageplot.set_visible_image_opacity(self.visible_image_opacity) def init_interface_data(self, data): same_domain = (self.data and data and data.domain == self.data.domain) if not same_domain: self.init_attr_values(data) self.init_visible_images(data) def output_image_selection(self): if not self.data: self.Outputs.selected_data.send(None) self.Outputs.annotated_data.send(None) self.curveplot.set_data(None) return indices = np.flatnonzero(self.imageplot.selection_group) annotated_data = groups_or_annotated_table( self.data, self.imageplot.selection_group) self.Outputs.annotated_data.send(annotated_data) selected = self.data[indices] self.Outputs.selected_data.send(selected if selected else None) if selected: self.curveplot.set_data(selected) else: self.curveplot.set_data(self.data) def init_attr_values(self, data): domain = data.domain if data is not None else None self.feature_value_model.set_domain(domain) self.attr_value = self.feature_value_model[ 0] if self.feature_value_model else None def init_visible_images(self, data): self.visible_image_model.clear() if data is not None and 'visible_images' in data.attributes: self.visbox.setEnabled(True) for img in data.attributes['visible_images']: self.visible_image_model.append(img) else: self.visbox.setEnabled(False) self.show_visible_image = False self.update_visible_image_interface() self._choose_visible_image() self.update_visible_image() def _choose_visible_image(self): # choose an image according to visible_image_name setting if len(self.visible_image_model): for img in self.visible_image_model: if img["name"] == self.visible_image_name: self.visible_image = img break else: self.visible_image = self.visible_image_model[0] def redraw_integral_info(self): di = {} integrate = self.image_values() if isinstance(integrate, Integrate) and np.any( self.curveplot.selection_group): # curveplot can have a subset of curves on the input> match IDs ind = np.flatnonzero(self.curveplot.selection_group)[0] dind = self.imageplot.data_ids[self.curveplot.data[ind].id] dshow = self.data[dind:dind + 1] datai = integrate(dshow) draw_info = datai.domain.attributes[0].compute_value.draw_info di = draw_info(dshow) self.refresh_markings(di) def refresh_markings(self, di): refresh_integral_markings([{ "draw": di }], self.markings_integral, self.curveplot) def image_values(self): if self.value_type == 0: # integrals imethod = self.integration_methods[self.integration_method] if imethod != Integrate.PeakAt: return Integrate(methods=imethod, limits=[[self.lowlim, self.highlim]]) else: return Integrate(methods=imethod, limits=[[self.choose, self.choose]]) else: return lambda data, attr=self.attr_value: \ data.transform(Domain([data.domain[attr]])) def image_values_fixed_levels(self): if self.value_type == 1 and isinstance(self.attr_value, DiscreteVariable): return 0, len(self.attr_value.values) - 1 return None def redraw_data(self): self.redraw_integral_info() self.imageplot.update_view() def update_feature_value(self): self.redraw_data() def _update_integration_type(self): self.line1.hide() self.line2.hide() self.line3.hide() if self.value_type == 0: self.box_values_spectra.setDisabled(False) self.box_values_feature.setDisabled(True) if self.integration_methods[ self.integration_method] != Integrate.PeakAt: self.line1.show() self.line2.show() else: self.line3.show() elif self.value_type == 1: self.box_values_spectra.setDisabled(True) self.box_values_feature.setDisabled(False) QTest.qWait(1) # first update the interface def _change_integration(self): # change what to show on the image self._update_integration_type() self.redraw_data() def changed_integral_range(self): if self.disable_integral_range: return self.redraw_data() def _change_integral_type(self): self._change_integration() @Inputs.data def set_data(self, data): self.closeContext() def valid_context(data): if data is None: return False annotation_features = [ v for v in data.domain.metas + data.domain.class_vars if isinstance(v, (DiscreteVariable, ContinuousVariable)) ] return len(annotation_features) >= 1 if valid_context(data): self.openContext(data) else: # to generate valid interface even if context was not loaded self.contextAboutToBeOpened.emit([data]) self.data = data self.imageplot.set_data(data) self.curveplot.set_data(data) self._init_integral_boundaries() self.imageplot.update_view() self.output_image_selection() self.update_visible_image() def _init_integral_boundaries(self): # requires data in curveplot self.disable_integral_range = True if self.curveplot.data_x is not None and len(self.curveplot.data_x): minx = self.curveplot.data_x[0] maxx = self.curveplot.data_x[-1] else: minx = 0. maxx = 1. if self.lowlim is None or not minx <= self.lowlim <= maxx: self.lowlim = minx self.line1.setValue(self.lowlim) if self.highlim is None or not minx <= self.highlim <= maxx: self.highlim = maxx self.line2.setValue(self.highlim) if self.choose is None: self.choose = (minx + maxx) / 2 elif self.choose < minx: self.choose = minx elif self.choose > maxx: self.choose = maxx self.line3.setValue(self.choose) self.disable_integral_range = False def save_graph(self): self.imageplot.save_graph() def onDeleteWidget(self): self.curveplot.shutdown() self.imageplot.shutdown() super().onDeleteWidget() def update_visible_image(self): img_info = self.visible_image if self.show_visible_image and img_info is not None: self.visible_image_name = img_info[ "name"] # save visual image name img = Image.open(img_info['image_ref']).convert('RGBA') # image must be vertically flipped # https://github.com/pyqtgraph/pyqtgraph/issues/315#issuecomment-214042453 # Behavior may change at pyqtgraph 1.0 version img = np.array(img)[::-1] width = img_info['img_size_x'] if 'img_size_x' in img_info \ else img.shape[1] * img_info['pixel_size_x'] height = img_info['img_size_y'] if 'img_size_y' in img_info \ else img.shape[0] * img_info['pixel_size_y'] rect = QRectF(img_info['pos_x'], img_info['pos_y'], width, height) self.imageplot.set_visible_image(img, rect) self.imageplot.show_visible_image() else: self.imageplot.hide_visible_image()
class OWHyper(OWWidget): name = "HyperSpectra" class Inputs: data = Input("Data", Orange.data.Table, default=True) class Outputs: selected_data = Output("Selection", Orange.data.Table, default=True) annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Orange.data.Table) icon = "icons/hyper.svg" priority = 20 replaces = ["orangecontrib.infrared.widgets.owhyper.OWHyper"] settings_version = 3 settingsHandler = DomainContextHandler() imageplot = SettingProvider(ImagePlot) curveplot = SettingProvider(CurvePlotHyper) integration_method = Setting(0) integration_methods = Integrate.INTEGRALS value_type = Setting(0) attr_value = ContextSetting(None) lowlim = Setting(None) highlim = Setting(None) choose = Setting(None) graph_name = "imageplot.plotview" # defined so that the save button is shown class Warning(OWWidget.Warning): threshold_error = Msg("Low slider should be less than High") class Error(OWWidget.Warning): image_too_big = Msg("Image for chosen features is too big ({} x {}).") @classmethod def migrate_settings(cls, settings_, version): if version < 2: # delete the saved attr_value to prevent crashes try: del settings_["context_settings"][0].values["attr_value"] except: pass # migrate selection if version <= 2: try: current_context = settings_["context_settings"][0] selection = getattr(current_context, "selection", None) if selection is not None: selection = [(i, 1) for i in np.flatnonzero(np.array(selection))] settings_.setdefault("imageplot", {})["selection_group_saved"] = selection except: pass def __init__(self): super().__init__() dbox = gui.widgetBox(self.controlArea, "Image values") rbox = gui.radioButtons( dbox, self, "value_type", callback=self._change_integration) gui.appendRadioButton(rbox, "From spectra") self.box_values_spectra = gui.indentedBox(rbox) gui.comboBox( self.box_values_spectra, self, "integration_method", valueType=int, items=(a.name for a in self.integration_methods), callback=self._change_integral_type) gui.rubber(self.controlArea) gui.appendRadioButton(rbox, "Use feature") self.box_values_feature = gui.indentedBox(rbox) self.feature_value_model = DomainModel(DomainModel.METAS | DomainModel.CLASSES, valid_types=DomainModel.PRIMITIVE) self.feature_value = gui.comboBox( self.box_values_feature, self, "attr_value", callback=self.update_feature_value, model=self.feature_value_model, sendSelectedValue=True, valueType=str) splitter = QSplitter(self) splitter.setOrientation(Qt.Vertical) self.imageplot = ImagePlot(self) self.imageplot.selection_changed.connect(self.output_image_selection) self.curveplot = CurvePlotHyper(self, select=SELECTONE) self.curveplot.selection_changed.connect(self.redraw_data) self.curveplot.plot.vb.x_padding = 0.005 # pad view so that lines are not hidden splitter.addWidget(self.imageplot) splitter.addWidget(self.curveplot) self.mainArea.layout().addWidget(splitter) self.line1 = MovableVline(position=self.lowlim, label="", report=self.curveplot) self.line1.sigMoved.connect(lambda v: setattr(self, "lowlim", v)) self.line2 = MovableVline(position=self.highlim, label="", report=self.curveplot) self.line2.sigMoved.connect(lambda v: setattr(self, "highlim", v)) self.line3 = MovableVline(position=self.choose, label="", report=self.curveplot) self.line3.sigMoved.connect(lambda v: setattr(self, "choose", v)) for line in [self.line1, self.line2, self.line3]: line.sigMoveFinished.connect(self.changed_integral_range) self.curveplot.add_marking(line) line.hide() self.data = None self.disable_integral_range = False self.resize(900, 700) self._update_integration_type() # prepare interface according to the new context self.contextAboutToBeOpened.connect(lambda x: self.init_interface_data(x[0])) def init_interface_data(self, data): same_domain = (self.data and data and data.domain == self.data.domain) if not same_domain: self.init_attr_values(data) def output_image_selection(self): if not self.data: self.Outputs.selected_data.send(None) self.Outputs.annotated_data.send(None) self.curveplot.set_data(None) return indices = np.flatnonzero(self.imageplot.selection_group) annotated_data = create_groups_table(self.data, self.imageplot.selection_group) if annotated_data is not None: annotated_data.X = self.data.X # workaround for Orange's copying on domain conversio self.Outputs.annotated_data.send(annotated_data) selected = self.data[indices] self.Outputs.selected_data.send(selected if selected else None) if selected: self.curveplot.set_data(selected) else: self.curveplot.set_data(self.data) def init_attr_values(self, data): domain = data.domain if data is not None else None self.feature_value_model.set_domain(domain) self.attr_value = self.feature_value_model[0] if self.feature_value_model else None def redraw_data(self): self.imageplot.update_view() def update_feature_value(self): self.redraw_data() def _update_integration_type(self): self.line1.hide() self.line2.hide() self.line3.hide() if self.value_type == 0: self.box_values_spectra.setDisabled(False) self.box_values_feature.setDisabled(True) if self.integration_methods[self.integration_method] != Integrate.PeakAt: self.line1.show() self.line2.show() else: self.line3.show() elif self.value_type == 1: self.box_values_spectra.setDisabled(True) self.box_values_feature.setDisabled(False) QTest.qWait(1) # first update the interface def _change_integration(self): # change what to show on the image self._update_integration_type() self.redraw_data() def changed_integral_range(self): if self.disable_integral_range: return self.redraw_data() def _change_integral_type(self): self._change_integration() @Inputs.data def set_data(self, data): self.closeContext() def valid_context(data): if data is None: return False annotation_features = [v for v in data.domain.metas + data.domain.class_vars if isinstance(v, (DiscreteVariable, ContinuousVariable))] return len(annotation_features) >= 1 if valid_context(data): self.openContext(data) else: # to generate valid interface even if context was not loaded self.contextAboutToBeOpened.emit([data]) self.data = data self.imageplot.set_data(data) self.curveplot.set_data(data) self._init_integral_boundaries() self.imageplot.update_view() self.output_image_selection() def _init_integral_boundaries(self): # requires data in curveplot self.disable_integral_range = True if self.curveplot.data_x is not None and len(self.curveplot.data_x): minx = self.curveplot.data_x[0] maxx = self.curveplot.data_x[-1] else: minx = 0. maxx = 1. if self.lowlim is None or not minx <= self.lowlim <= maxx: self.lowlim = minx self.line1.setValue(self.lowlim) if self.highlim is None or not minx <= self.highlim <= maxx: self.highlim = maxx self.line2.setValue(self.highlim) if self.choose is None: self.choose = (minx + maxx)/2 elif self.choose < minx: self.choose = minx elif self.choose > maxx: self.choose = maxx self.line3.setValue(self.choose) self.disable_integral_range = False def save_graph(self): # directly call save_graph so it hides axes self.imageplot.save_graph()
class OWHyper(OWWidget): name = "HyperSpectra" class Inputs: data = Input("Data", Orange.data.Table, default=True) class Outputs: selected_data = Output("Selection", Orange.data.Table, default=True) annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Orange.data.Table) icon = "icons/hyper.svg" priority = 20 replaces = ["orangecontrib.infrared.widgets.owhyper.OWHyper"] settings_version = 3 settingsHandler = DomainContextHandler() imageplot = SettingProvider(ImagePlot) curveplot = SettingProvider(CurvePlotHyper) integration_method = Setting(0) integration_methods = Integrate.INTEGRALS value_type = Setting(0) attr_value = ContextSetting(None) lowlim = Setting(None) highlim = Setting(None) choose = Setting(None) graph_name = "imageplot.plotview" # defined so that the save button is shown class Warning(OWWidget.Warning): threshold_error = Msg("Low slider should be less than High") class Error(OWWidget.Warning): image_too_big = Msg("Image for chosen features is too big ({} x {}).") @classmethod def migrate_settings(cls, settings_, version): if version < 2: # delete the saved attr_value to prevent crashes try: del settings_["context_settings"][0].values["attr_value"] except: pass # migrate selection if version <= 2: try: current_context = settings_["context_settings"][0] selection = getattr(current_context, "selection", None) if selection is not None: selection = [(i, 1) for i in np.flatnonzero(np.array(selection))] settings_.setdefault("imageplot", {})["selection_group_saved"] = selection except: pass def __init__(self): super().__init__() dbox = gui.widgetBox(self.controlArea, "Image values") rbox = gui.radioButtons( dbox, self, "value_type", callback=self._change_integration) gui.appendRadioButton(rbox, "From spectra") self.box_values_spectra = gui.indentedBox(rbox) gui.comboBox( self.box_values_spectra, self, "integration_method", valueType=int, items=(a.name for a in self.integration_methods), callback=self._change_integral_type) gui.rubber(self.controlArea) gui.appendRadioButton(rbox, "Use feature") self.box_values_feature = gui.indentedBox(rbox) self.feature_value_model = DomainModel(DomainModel.METAS | DomainModel.CLASSES, valid_types=DomainModel.PRIMITIVE) self.feature_value = gui.comboBox( self.box_values_feature, self, "attr_value", callback=self.update_feature_value, model=self.feature_value_model, sendSelectedValue=True, valueType=str) splitter = QSplitter(self) splitter.setOrientation(Qt.Vertical) self.imageplot = ImagePlot(self) self.imageplot.selection_changed.connect(self.output_image_selection) self.curveplot = CurvePlotHyper(self, select=SELECTONE) self.curveplot.selection_changed.connect(self.redraw_data) self.curveplot.plot.vb.x_padding = 0.005 # pad view so that lines are not hidden splitter.addWidget(self.imageplot) splitter.addWidget(self.curveplot) self.mainArea.layout().addWidget(splitter) self.line1 = MovableVline(position=self.lowlim, label="", report=self.curveplot) self.line1.sigMoved.connect(lambda v: setattr(self, "lowlim", v)) self.line2 = MovableVline(position=self.highlim, label="", report=self.curveplot) self.line2.sigMoved.connect(lambda v: setattr(self, "highlim", v)) self.line3 = MovableVline(position=self.choose, label="", report=self.curveplot) self.line3.sigMoved.connect(lambda v: setattr(self, "choose", v)) for line in [self.line1, self.line2, self.line3]: line.sigMoveFinished.connect(self.changed_integral_range) self.curveplot.add_marking(line) line.hide() self.data = None self.disable_integral_range = False self.resize(900, 700) self._update_integration_type() # prepare interface according to the new context self.contextAboutToBeOpened.connect(lambda x: self.init_interface_data(x[0])) def init_interface_data(self, data): same_domain = (self.data and data and data.domain == self.data.domain) if not same_domain: self.init_attr_values(data) def output_image_selection(self): if not self.data: self.Outputs.selected_data.send(None) self.Outputs.annotated_data.send(None) self.curveplot.set_data(None) return indices = np.flatnonzero(self.imageplot.selection_group) annotated_data = groups_or_annotated_table(self.data, self.imageplot.selection_group) self.Outputs.annotated_data.send(annotated_data) selected = self.data[indices] self.Outputs.selected_data.send(selected if selected else None) if selected: self.curveplot.set_data(selected) else: self.curveplot.set_data(self.data) def init_attr_values(self, data): domain = data.domain if data is not None else None self.feature_value_model.set_domain(domain) self.attr_value = self.feature_value_model[0] if self.feature_value_model else None def redraw_data(self): self.imageplot.update_view() def update_feature_value(self): self.redraw_data() def _update_integration_type(self): self.line1.hide() self.line2.hide() self.line3.hide() if self.value_type == 0: self.box_values_spectra.setDisabled(False) self.box_values_feature.setDisabled(True) if self.integration_methods[self.integration_method] != Integrate.PeakAt: self.line1.show() self.line2.show() else: self.line3.show() elif self.value_type == 1: self.box_values_spectra.setDisabled(True) self.box_values_feature.setDisabled(False) QTest.qWait(1) # first update the interface def _change_integration(self): # change what to show on the image self._update_integration_type() self.redraw_data() def changed_integral_range(self): if self.disable_integral_range: return self.redraw_data() def _change_integral_type(self): self._change_integration() @Inputs.data def set_data(self, data): self.closeContext() def valid_context(data): if data is None: return False annotation_features = [v for v in data.domain.metas + data.domain.class_vars if isinstance(v, (DiscreteVariable, ContinuousVariable))] return len(annotation_features) >= 1 if valid_context(data): self.openContext(data) else: # to generate valid interface even if context was not loaded self.contextAboutToBeOpened.emit([data]) self.data = data self.imageplot.set_data(data) self.curveplot.set_data(data) self._init_integral_boundaries() self.imageplot.update_view() self.output_image_selection() def _init_integral_boundaries(self): # requires data in curveplot self.disable_integral_range = True if self.curveplot.data_x is not None and len(self.curveplot.data_x): minx = self.curveplot.data_x[0] maxx = self.curveplot.data_x[-1] else: minx = 0. maxx = 1. if self.lowlim is None or not minx <= self.lowlim <= maxx: self.lowlim = minx self.line1.setValue(self.lowlim) if self.highlim is None or not minx <= self.highlim <= maxx: self.highlim = maxx self.line2.setValue(self.highlim) if self.choose is None: self.choose = (minx + maxx)/2 elif self.choose < minx: self.choose = minx elif self.choose > maxx: self.choose = maxx self.line3.setValue(self.choose) self.disable_integral_range = False def save_graph(self): self.imageplot.save_graph()