Exemplo n.º 1
0
    def __init__(self, plugin, title, text=None):
        """
        Creates a modal window. The return code is the button number that was
          last pressed before closing the window.
        plugin (Plugin): The plugin creating this dialog
        title (str): The title of the window
        text (None or str): If provided, it is displayed at the top of the window
        """
        super(AcquisitionDialog, self).__init__(plugin.main_app.main_frame)

        logging.debug("Creating acquisition dialog for %s",
                      plugin.__class__.__name__)
        self.plugin = plugin

        self.SetTitle(title)

        if text is not None:
            self.lbl_description = AutoWrapStaticText(self.pnl_desc, "")
            self.lbl_description.SetBackgroundColour(
                self.pnl_desc.GetBackgroundColour())
            self.lbl_description.SetForegroundColour(gui.FG_COLOUR_MAIN)
            self.pnl_desc.GetSizer().Add(self.lbl_description,
                                         flag=wx.EXPAND | wx.ALL,
                                         border=10)
            self.lbl_description.SetLabel(text)

        self._acq_future_connector = None
        self.buttons = []  # The buttons
        self.current_future = None
        self.btn_cancel.Bind(wx.EVT_BUTTON, self._cancel_future)

        self.setting_controller = SettingsController(self.fp_settings,
                                                     "No settings defined")

        # Create a minimal model for use in the streambar controller

        self._dmodel = MicroscopyGUIData(plugin.main_app.main_data)
        self.hidden_view = StreamView("Plugin View Hidden")
        self.view = MicroscopeView("Plugin View left")
        self.viewport_l.setView(self.view, self._dmodel)
        self.view_r = MicroscopeView("Plugin View right")
        self.viewport_r.setView(self.view_r, self._dmodel)
        self.spectrum_view = MicroscopeView("Plugin View spectrum")
        self.spectrum_viewport.setView(self.spectrum_view, self._dmodel)
        self._dmodel.focussedView.value = self.view
        self._dmodel.views.value = [self.view, self.view_r, self.spectrum_view]
        self._viewports = (self.viewport_l, self.viewport_r,
                           self.spectrum_viewport)

        self.streambar_controller = StreamBarController(self._dmodel,
                                                        self.pnl_streams,
                                                        ignore_view=True)

        self.Fit()
Exemplo n.º 2
0
    def __init__(self, plugin, title, text=None):
        """
        Creates a modal window. The return code is the button number that was
          last pressed before closing the window.
        title (str): The title of the window
        text (None or str): If provided, it is displayed at the top of the window
        """

        super(AcquisitionDialog, self).__init__(plugin.main_app.main_frame)

        self.plugin = plugin

        self.SetTitle(title)

        if text is not None:
            self.lbl_description = AutoWrapStaticText(self.pnl_desc, "")
            self.lbl_description.SetBackgroundColour(
                self.pnl_desc.GetBackgroundColour())
            self.pnl_desc.GetSizer().Add(self.lbl_description,
                                         flag=wx.EXPAND | wx.ALL,
                                         border=10)
            self.lbl_description.SetLabel(text)

        self.entries = []  # Setting entries
        self._acq_future_connector = None
        self.canvas = None
        self.buttons = []  # The buttons
        self.current_future = None
        self.btn_cancel.Bind(wx.EVT_BUTTON, self._cancel_future)

        self.setting_controller = SettingsController(self.fp_settings,
                                                     "No settings defined")

        # Create a minimal model for use in the streambar controller

        data_model = MicroscopyGUIData(plugin.main_app.main_data)
        self.microscope_view = MicroscopeView("Plugin View")
        data_model.focussedView = VigilantAttribute(self.microscope_view)
        self.viewport.setView(self.microscope_view, data_model)

        self.streambar_controller = StreamBarController(data_model,
                                                        self.pnl_streams,
                                                        ignore_view=True)

        self.Refresh()
        self.Fit()
Exemplo n.º 3
0
    def __init__(self, plugin, title, text=None):
        """
        Creates a modal window. The return code is the button number that was
          last pressed before closing the window.
        title (str): The title of the window
        text (None or str): If provided, it is displayed at the top of the window
        """

        super(AcquisitionDialog, self).__init__(plugin.main_app.main_frame)

        self.plugin = plugin

        self.SetTitle(title)

        if text is not None:
            self.lbl_description = AutoWrapStaticText(self.pnl_desc, "")
            self.lbl_description.SetBackgroundColour(self.pnl_desc.GetBackgroundColour())
            self.pnl_desc.GetSizer().Add(self.lbl_description, flag=wx.EXPAND | wx.ALL, border=10)
            self.lbl_description.SetLabel(text)

        self._acq_future_connector = None
        self.buttons = []  # The buttons
        self.current_future = None
        self.btn_cancel.Bind(wx.EVT_BUTTON, self._cancel_future)

        self.setting_controller = SettingsController(self.fp_settings,
                                                     "No settings defined")

        # Create a minimal model for use in the streambar controller

        self._dmodel = MicroscopyGUIData(plugin.main_app.main_data)
        self.hidden_view = StreamView("Plugin View Hidden")
        self.view = MicroscopeView("Plugin View left")
        self.viewport_l.setView(self.view, self._dmodel)
        self.view_r = MicroscopeView("Plugin View right")
        self.viewport_r.setView(self.view_r, self._dmodel)
        self.spectrum_view = MicroscopeView("Plugin View spectrum")
        self.spectrum_viewport.setView(self.spectrum_view, self._dmodel)
        self._dmodel.focussedView.value = self.view
        self._dmodel.views.value = [self.view, self.view_r,
                                    self.spectrum_view]
        self._viewports = (self.viewport_l, self.viewport_r, self.spectrum_viewport)

        self.streambar_controller = StreamBarController(
            self._dmodel,
            self.pnl_streams,
            ignore_view=True
        )

        self.Fit()
Exemplo n.º 4
0
    def __init__(self, plugin, title, text=None):
        """
        Creates a modal window. The return code is the button number that was
          last pressed before closing the window.
        title (str): The title of the window
        text (None or str): If provided, it is displayed at the top of the window
        """

        super(AcquisitionDialog, self).__init__(plugin.main_app.main_frame)

        self.plugin = plugin

        self.SetTitle(title)

        if text is not None:
            self.lbl_description = AutoWrapStaticText(self.pnl_desc, "")
            self.lbl_description.SetBackgroundColour(self.pnl_desc.GetBackgroundColour())
            self.pnl_desc.GetSizer().Add(self.lbl_description, flag=wx.EXPAND | wx.ALL, border=10)
            self.lbl_description.SetLabel(text)

        self.entries = []  # Setting entries
        self._acq_future_connector = None
        self.canvas = None
        self.buttons = []  # The buttons

        self.setting_controller = SettingsController(self.fp_settings,
                                                     "No settings defined")

        # Create a minimal model for use in the streambar controller

        data_model = MicroscopyGUIData(plugin.main_app.main_data)
        self.microscope_view = MicroscopeView("Plugin View")
        data_model.focussedView = VigilantAttribute(self.microscope_view)
        self.viewport.setView(self.microscope_view, data_model)

        self.streambar_controller = StreamBarController(
            data_model,
            self.pnl_streams,
            ignore_view=True
        )

        self.Refresh()
        self.Fit()
Exemplo n.º 5
0
    def __init__(self, plugin, title, text=None, stage=None, fov_hw=None):
        """
        Creates a modal window. The return code is the button number that was
          last pressed before closing the window.
        title (str): The title of the window
        text (None or str): If provided, it is displayed at the top of the window
        stage (None or actuator with x/y axes)
        fov_hw=None
        """
        xrcfr_plugin.__init__(self, plugin.main_app.main_frame)

        self.plugin = plugin

        self.SetTitle(title)

        self._acq_future_connector = None
        self.canvas = None
        self.buttons = []  # The buttons
        self.current_future = None
        self.btn_cancel.Bind(wx.EVT_BUTTON, self._cancel_future)

        self.setting_controller = SettingsController(self.fp_settings,
                                                     "No settings defined")

        # Create a minimal model for use in the streambar controller

        self._dmodel = MicroscopyGUIData(plugin.main_app.main_data)
        self.view = ContentView("Plugin View left", stage=stage, fov_hw=fov_hw)
        self.viewport_l.setView(self.view, self._dmodel)
        self._dmodel.focussedView.value = self.view
        self._dmodel.views.value = [self.view]
        self._viewports = (self.viewport_l, )

        self.streambar_controller = StreamBarController(self._dmodel,
                                                        self.pnl_streams,
                                                        ignore_view=True)

        self.Refresh()
        self.Fit()
Exemplo n.º 6
0
class AcquisitionDialog(xrcfr_plugin):
    def __init__(self, plugin, title, text=None):
        """
        Creates a modal window. The return code is the button number that was
          last pressed before closing the window.
        title (str): The title of the window
        text (None or str): If provided, it is displayed at the top of the window
        """

        super(AcquisitionDialog, self).__init__(plugin.main_app.main_frame)

        self.plugin = plugin

        self.SetTitle(title)

        if text is not None:
            self.lbl_description = AutoWrapStaticText(self.pnl_desc, "")
            self.lbl_description.SetBackgroundColour(self.pnl_desc.GetBackgroundColour())
            self.pnl_desc.GetSizer().Add(self.lbl_description, flag=wx.EXPAND | wx.ALL, border=10)
            self.lbl_description.SetLabel(text)

        self.entries = []  # Setting entries
        self._acq_future_connector = None
        self.canvas = None
        self.buttons = []  # The buttons

        self.setting_controller = SettingsController(self.fp_settings,
                                                     "No settings defined")

        # Create a minimal model for use in the streambar controller

        data_model = MicroscopyGUIData(plugin.main_app.main_data)
        self.microscope_view = MicroscopeView("Plugin View")
        data_model.focussedView = VigilantAttribute(self.microscope_view)
        self.viewport.setView(self.microscope_view, data_model)

        self.streambar_controller = StreamBarController(
            data_model,
            self.pnl_streams,
            ignore_view=True
        )

        self.Refresh()
        self.Fit()

    @call_in_wx_main
    def addSettings(self, objWithVA, conf=None):
        """
        Adds settings as one widget on a line for each VigilantAttribute (VA) in
         the given object. Each setting entry created is added to .entries.
        objWithVA (object): an object with VAs.

        conf (None or dict of str->config): allows to override the automatic
          selection of the VA widget. See odemis.gui.conf.data for documentation.

        raise:
            LookupError: if no VA is found on the objWithVA

        """
        vas = getVAs(objWithVA)
        if not vas:
            raise LookupError("No VAs found!")

        if not conf:
            conf = {}
        vas_names = util.sorted_according_to(vas.keys(), conf.keys())

        for name in vas_names:
            va = vas[name]
            self.setting_controller.add_setting_entry(name, va, None,
                                                      conf=conf.get(name, None))

    @call_in_wx_main
    def addButton(self, label, callback=None, face_colour='def'):
        """
        Add a button at the bottom right of the window. If some buttons are already
        present, they are shifted to the left.

        label (str): text on the button,
        callback (None or callable): the function to be called when the button
          is pressed (with the dialog as argument). If callback is None,
          pressing the button will close the window and the button number will
          be the return code of the dialog.

        """
        btnid = len(self.buttons)
        btn = ImageTextButton(self.pnl_buttons, label=label, height=48,
                              style=wx.ALIGN_CENTER, face_colour=face_colour)
        self.buttons.append(btn)
        sizer = self.pnl_buttons.GetSizer()
        sizer.Add(btn, proportion=1, flag=wx.ALL | wx.ALIGN_RIGHT, border=10)

        if callback is not None and callable(callback):
            # Wrap the callback, to run in a separate thread, so it doesn't block
            # the GUI.
            def button_callback_wrapper(evt, btnid=btnid):
                try:
                    self.SetReturnCode(btnid)
                    t = threading.Thread(target=callback, args=(self,),
                                         name="Callback for button %s" % (label,))
                    t.start()
                except Exception:
                    logging.exception("Error when processing button %s of plugin %s",
                                      label, self.plugin)
            btn.Bind(wx.EVT_BUTTON, button_callback_wrapper)
        else:
            btn.Bind(wx.EVT_BUTTON, partial(self.on_close, btnid))

        self.Fit()

    @call_in_wx_main
    def addStream(self, stream):
        """
        Adds a stream to the canvas, and a stream entry to the stream panel.
        It also ensures the panel box and canvas are shown.

        Note: If this method is not called, the stream panel and canvas are hidden.

        returns (StreamController): the stream entry

        """

        if not self.fp_streams.IsShown() or not self.viewport.IsShown():
            self.fp_streams.Show()
            self.viewport.Show()
            self.Layout()
            self.Fit()
            self.Update()

        if stream:
            self.streambar_controller.addStream(stream)
            self.microscope_view.addStream(stream)

    @call_in_wx_main
    def showProgress(self, future):
        """
        Shows a progress bar, based on the status of the progressive future given.
        As long as the future is not finished, the buttons are disabled.

        future (None or Future): The progressive future to show the progress with
          the progress bar. If future is None, it will hide the progress bar.
          If future is cancellable, show a cancel button next to the progress bar.

        """

        if future is None:
            self.gauge_progress.Hide()
            self.lbl_gauge.Hide()
        else:
            self.gauge_progress.Show()
            self.lbl_gauge.Show()

        self.Layout()
        self.Update()

        if future is None:
            self._acq_future_connector = None
            return
        elif hasattr(future, "add_update_callback"):  # ProgressiveFuture
            self._acq_future_connector = ProgressiveFutureConnector(future,
                                                                    self.gauge_progress,
                                                                    self.lbl_gauge)
        else:
            # TODO: just pulse the gauge at a "good" frequency (need to use a timer)
            self.gauge_progress.Pulse()

        # future.add_done_callback(self._on_future_done)

        # TODO: if the future is cancellable (ie, has task_canceller), allow to
        # press the "cancel" button, if such button exists, otherwise provide
        # such a button. That button will call cancel() on the future.

    @call_in_wx_main
    def _on_future_done(self, _):
        """ Hide the gauge and label when the future finishes """
        self.gauge_progress.Hide()
        self.lbl_gauge.Hide()
        self.Layout()
        self.Update()

    def on_close(self, btnid, _):
        self.EndModal(btnid)

    @call_in_wx_main
    def Destroy(self, *args, **kwargs):
        # save the return code, as Destroy() automatically sets it to wx.ID_CANCEL
        # but we want to keep the value potentially set by the button.
        rc = self.ReturnCode
        super(AcquisitionDialog, self).Destroy(*args, **kwargs)
        self.ReturnCode = rc
Exemplo n.º 7
0
class AcquisitionDialog(xrcfr_plugin):
    def __init__(self, plugin, title, text=None):
        """
        Creates a modal window. The return code is the button number that was
          last pressed before closing the window.
        title (str): The title of the window
        text (None or str): If provided, it is displayed at the top of the window
        """

        super(AcquisitionDialog, self).__init__(plugin.main_app.main_frame)

        self.plugin = plugin

        self.SetTitle(title)

        if text is not None:
            self.lbl_description = AutoWrapStaticText(self.pnl_desc, "")
            self.lbl_description.SetBackgroundColour(
                self.pnl_desc.GetBackgroundColour())
            self.pnl_desc.GetSizer().Add(self.lbl_description,
                                         flag=wx.EXPAND | wx.ALL,
                                         border=10)
            self.lbl_description.SetLabel(text)

        self.entries = []  # Setting entries
        self._acq_future_connector = None
        self.canvas = None
        self.buttons = []  # The buttons
        self.current_future = None
        self.btn_cancel.Bind(wx.EVT_BUTTON, self._cancel_future)

        self.setting_controller = SettingsController(self.fp_settings,
                                                     "No settings defined")

        # Create a minimal model for use in the streambar controller

        data_model = MicroscopyGUIData(plugin.main_app.main_data)
        self.microscope_view = MicroscopeView("Plugin View")
        data_model.focussedView = VigilantAttribute(self.microscope_view)
        self.viewport.setView(self.microscope_view, data_model)

        self.streambar_controller = StreamBarController(data_model,
                                                        self.pnl_streams,
                                                        ignore_view=True)

        self.Refresh()
        self.Fit()

    @call_in_wx_main
    def addSettings(self, objWithVA, conf=None):
        """
        Adds settings as one widget on a line for each VigilantAttribute (VA) in
         the given object. Each setting entry created is added to .entries.
        objWithVA (object): an object with VAs.

        conf (None or dict of str->config): allows to override the automatic
          selection of the VA widget. See odemis.gui.conf.data for documentation.

        raise:
            LookupError: if no VA is found on the objWithVA

        """
        vas = getVAs(objWithVA)
        if not vas:
            raise LookupError("No VAs found!")

        if not conf:
            conf = {}
        vas_names = util.sorted_according_to(vas.keys(), conf.keys())

        for name in vas_names:
            va = vas[name]
            self.setting_controller.add_setting_entry(name,
                                                      va,
                                                      None,
                                                      conf=conf.get(
                                                          name, None))

    @call_in_wx_main
    def addButton(self, label, callback=None, face_colour='def'):
        """
        Add a button at the bottom right of the window. If some buttons are already
        present, they are shifted to the left.

        label (str): text on the button,
        callback (None or callable): the function to be called when the button
          is pressed (with the dialog as argument). If callback is None,
          pressing the button will close the window and the button number will
          be the return code of the dialog.

        """
        btnid = len(self.buttons)
        btn = ImageTextButton(self.pnl_buttons,
                              label=label,
                              height=48,
                              style=wx.ALIGN_CENTER,
                              face_colour=face_colour)
        self.buttons.append(btn)
        sizer = self.pnl_buttons.GetSizer()
        sizer.Add(btn, proportion=1, flag=wx.ALL | wx.ALIGN_RIGHT, border=10)

        if callback is not None and callable(callback):
            # Wrap the callback, to run in a separate thread, so it doesn't block
            # the GUI.
            def button_callback_wrapper(evt, btnid=btnid):
                try:
                    self.SetReturnCode(btnid)
                    t = threading.Thread(target=callback,
                                         args=(self, ),
                                         name="Callback for button %s" %
                                         (label, ))
                    t.start()
                except Exception:
                    logging.exception(
                        "Error when processing button %s of plugin %s", label,
                        self.plugin)

            btn.Bind(wx.EVT_BUTTON, button_callback_wrapper)
        else:
            btn.Bind(wx.EVT_BUTTON, partial(self.on_close, btnid))

        self.Fit()

    @call_in_wx_main
    def addStream(self, stream):
        """
        Adds a stream to the canvas, and a stream entry to the stream panel.
        It also ensures the panel box and canvas are shown.

        Note: If this method is not called, the stream panel and canvas are hidden.

        returns (StreamController): the stream entry

        """

        if not self.fp_streams.IsShown() or not self.viewport.IsShown():
            self.fp_streams.Show()
            self.viewport.Show()
            self.Layout()
            self.Fit()
            self.Update()

        if stream:
            self.streambar_controller.addStream(stream)
            self.microscope_view.addStream(stream)

    @call_in_wx_main
    def showProgress(self, future):
        """
        Shows a progress bar, based on the status of the progressive future given.
        As long as the future is not finished, the buttons are disabled.

        future (None or Future): The progressive future to show the progress with
          the progress bar. If future is None, it will hide the progress bar.
          If future is cancellable, show a cancel button next to the progress bar.

        """

        if future is not None and not future.cancelled():
            self.current_future = future
            self.enable_buttons(False)

        self.Layout()
        self.Update()

        if self.current_future is None:
            self._acq_future_connector = None
            return
        else:
            if hasattr(self.current_future, "add_update_callback"):
                self._acq_future_connector = ProgressiveFutureConnector(
                    self.current_future, self.gauge_progress, self.lbl_gauge)
            else:
                # TODO: just pulse the gauge at a "good" frequency (need to use a timer)
                self.gauge_progress.Pulse()

            if hasattr(self.current_future, 'task_canceller'):
                self.btn_cancel.Enable()
            else:
                self.btn_cancel.Disable()

        future.add_done_callback(self._on_future_done)

        # TODO: if the future is cancellable (ie, has task_canceller), allow to
        # press the "cancel" button, if such button exists, otherwise provide
        # such a button. That button will call cancel() on the future.

    @call_in_wx_main
    def enable_buttons(self, enable):
        """ Enable or disable all the buttons in the button panel """
        for btn in self.pnl_buttons.GetChildren():
            btn.Enable(enable)

    def _cancel_future(self, _):
        """ Cancel the future if it's there and running """
        if self.current_future is not None and not self.current_future.cancelled(
        ):
            if self.current_future.cancel():
                logging.debug("Future cancelled")
            else:
                logging.debug("Failed to cancel future")

    @call_in_wx_main
    def _on_future_done(self, _):
        """ Hide the gauge and label when the future finishes """
        self.gauge_progress.SetValue(0)
        self.lbl_gauge.SetLabel("")
        self.btn_cancel.Disable()
        self.enable_buttons(True)
        self.Layout()
        self.Update()

    def on_close(self, btnid, _):
        logging.debug("Closing window")
        self.streambar_controller.clear()
        self.EndModal(btnid)

    @call_in_wx_main
    def Destroy(self, *args, **kwargs):
        self.streambar_controller.clear()
        # save the return code, as Destroy() automatically sets it to wx.ID_CANCEL
        # but we want to keep the value potentially set by the button.
        rc = self.ReturnCode
        logging.debug("Destroying acquisition dialog")
        super(AcquisitionDialog, self).Destroy(*args, **kwargs)
        self.ReturnCode = rc
        logging.debug("Dialog destroyed")
Exemplo n.º 8
0
class AcquisitionDialog(xrcfr_plugin):
    def __init__(self, plugin, title, text=None):
        """
        Creates a modal window. The return code is the button number that was
          last pressed before closing the window.
        plugin (Plugin): The plugin creating this dialog
        title (str): The title of the window
        text (None or str): If provided, it is displayed at the top of the window
        """
        super(AcquisitionDialog, self).__init__(plugin.main_app.main_frame)

        logging.debug("Creating acquisition dialog for %s",
                      plugin.__class__.__name__)
        self.plugin = plugin

        self.SetTitle(title)

        if text is not None:
            self.lbl_description = AutoWrapStaticText(self.pnl_desc, "")
            self.lbl_description.SetBackgroundColour(
                self.pnl_desc.GetBackgroundColour())
            self.lbl_description.SetForegroundColour(gui.FG_COLOUR_MAIN)
            self.pnl_desc.GetSizer().Add(self.lbl_description,
                                         flag=wx.EXPAND | wx.ALL,
                                         border=10)
            self.lbl_description.SetLabel(text)

        self._acq_future_connector = None
        self.buttons = []  # The buttons
        self.current_future = None
        self.btn_cancel.Bind(wx.EVT_BUTTON, self._cancel_future)

        self.setting_controller = SettingsController(self.fp_settings,
                                                     "No settings defined")

        # Create a minimal model for use in the streambar controller

        self._dmodel = MicroscopyGUIData(plugin.main_app.main_data)
        self.hidden_view = StreamView("Plugin View Hidden")
        self.view = MicroscopeView("Plugin View left")
        self.viewport_l.setView(self.view, self._dmodel)
        self.view_r = MicroscopeView("Plugin View right")
        self.viewport_r.setView(self.view_r, self._dmodel)
        self.spectrum_view = MicroscopeView("Plugin View spectrum")
        self.spectrum_viewport.setView(self.spectrum_view, self._dmodel)
        self._dmodel.focussedView.value = self.view
        self._dmodel.views.value = [self.view, self.view_r, self.spectrum_view]
        self._viewports = (self.viewport_l, self.viewport_r,
                           self.spectrum_viewport)

        self.streambar_controller = StreamBarController(self._dmodel,
                                                        self.pnl_streams,
                                                        ignore_view=True)

        self.Fit()

    @call_in_wx_main
    def addSettings(self, objWithVA, conf=None):
        """
        Adds settings as one widget on a line for each VigilantAttribute (VA) in
         the given object. Each setting entry created is added to setting_controller.entries.
        objWithVA (object): an object with VAs.

        conf (None or dict of str->config): allows to override the automatic
          selection of the VA widget. See odemis.gui.conf.data for documentation.

        raise:
            LookupError: if no VA is found on the objWithVA

        """
        vas = getVAs(objWithVA)
        if not vas:
            raise LookupError("No VAs found!")

        if not conf:
            conf = {}
        vas_names = util.sorted_according_to(list(vas.keys()),
                                             list(conf.keys()))

        for name in vas_names:
            va = vas[name]
            self.setting_controller.add_setting_entry(name,
                                                      va,
                                                      None,
                                                      conf=conf.get(
                                                          name, None))

        self.Layout()

    @call_in_wx_main
    def addButton(self, label, callback=None, face_colour='def'):
        """
        Add a button at the bottom right of the window. If some buttons are already
        present, they are shifted to the left.

        label (str): text on the button,
        callback (None or callable): the function to be called when the button
          is pressed (with the dialog as argument). If callback is None,
          pressing the button will close the window and the button number will
          be the return code of the dialog.

        """
        btnid = len(self.buttons)
        btn = ImageTextButton(self.pnl_buttons,
                              label=label,
                              height=48,
                              style=wx.ALIGN_CENTER,
                              face_colour=face_colour)
        self.buttons.append(btn)
        sizer = self.pnl_buttons.GetSizer()
        sizer.Add(btn, proportion=1, flag=wx.ALL, border=10)

        if callback is not None and callable(callback):
            # Wrap the callback, to run in a separate thread, so it doesn't block
            # the GUI.
            def button_callback_wrapper(evt, btnid=btnid):
                # TODO: disable the button while the callback is running, so that
                # it's not possible for the user to press twice in a row and cause
                # the code to run twice simultaneously (without very explicitly
                # allowing that).
                try:
                    self.SetReturnCode(btnid)
                    logging.info("Button '%s' handled by %s, %s", label,
                                 self.plugin.__class__.__name__, callback)
                    t = threading.Thread(target=callback,
                                         args=(self, ),
                                         name="Callback for button %s" %
                                         (label, ))
                    t.start()
                except Exception:
                    logging.exception(
                        "Error when processing button %s of plugin %s", label,
                        self.plugin)

            btn.Bind(wx.EVT_BUTTON, button_callback_wrapper)
        else:
            btn.Bind(wx.EVT_BUTTON, partial(self.on_close, btnid))

        self.pnl_buttons.Layout()

    @call_in_wx_main
    def addStream(self, stream, index=0):
        """
        Adds a stream to the viewport, and a stream entry to the stream panel.
        It also ensures the panel box and viewport are shown.

        Note: If this method is not called, the stream panel and viewports are hidden.

        stream (Stream or None): Stream to be added. Use None to force a viewport
          to be seen without adding a stream.
        index (0, 1, 2, or None): Index of the viewport to add the stream. 0 = left,
          1 = right, 2 = spectrum viewport. If None, it will not show the stream
          on any viewport (and it will be added to the .hidden_view)
        """
        need_layout = False

        if index is None:
            v = self.hidden_view
        else:
            viewport = self._viewports[index]
            v = self._dmodel.views.value[index]
            assert viewport.view is v

            if not viewport.IsShown():
                viewport.Show()
                need_layout = True

        if stream:
            if not self.fp_streams.IsShown():
                self.fp_streams.Show()
                need_layout = True
            self.streambar_controller.addStream(stream, add_to_view=v)

        if need_layout:
            self.Layout()
            self.Update()

    @call_in_wx_main
    def showProgress(self, future):
        """
        Shows a progress bar, based on the status of the progressive future given.
        As long as the future is not finished, the buttons are disabled.

        future (None or Future): The progressive future to show the progress with
          the progress bar. If future is None, it will hide the progress bar.
          If future is cancellable, show a cancel button next to the progress bar.

        """
        if future is not None and not future.cancelled():
            self.current_future = future
            self.enable_buttons(False)

        self.pnl_gauge.Show(future is not None)
        self.Layout()

        if self.current_future is None:
            self._acq_future_connector = None
            return
        else:
            if hasattr(self.current_future, "add_update_callback"):
                self._acq_future_connector = ProgressiveFutureConnector(
                    self.current_future, self.gauge_progress, self.lbl_gauge)
            else:
                # TODO: just pulse the gauge at a "good" frequency (need to use a timer)
                self.gauge_progress.Pulse()

            # If the future is cancellable (ie, has task_canceller), allow to
            # press the "cancel" button, which will call cancel() on the future.
            # TODO: if there is already a "cancel" button in the window, use it
            # instead a providing another one.
            if hasattr(self.current_future, 'task_canceller'):
                self.btn_cancel.Enable()
            else:
                self.btn_cancel.Disable()

        future.add_done_callback(self._on_future_done)

    @call_in_wx_main
    def setAcquisitionInfo(self, text=None, lvl=logging.INFO):
        """
        Displays acquisition info above progress bar.
        text (str or None): text to be displayed. If None is passed, the acquisition
        label will be hidden, so no empty space is displayed.
        lvl (int, from logging.*): log level, which selects the display colour.
        Options: logging.INFO, logging.WARNING, logging.ERROR
        """
        if text is None:
            self.pnl_info.Hide()
        else:
            self.lbl_acquisition_info.SetLabel(text)
            if lvl >= logging.ERROR:
                self.lbl_acquisition_info.SetForegroundColour(FG_COLOUR_ERROR)
            elif lvl >= logging.WARNING:
                self.lbl_acquisition_info.SetForegroundColour(
                    FG_COLOUR_WARNING)
            else:
                self.lbl_acquisition_info.SetForegroundColour(FG_COLOUR_MAIN)
            self.pnl_info.Show()

        self.Layout()

    @call_in_wx_main
    def pauseSettings(self):
        """ Pause the settings widgets. They will be disabled and the value frozen even when the VAs are changed """
        self.setting_controller.pause()
        self.streambar_controller.pause()

    @call_in_wx_main
    def resumeSettings(self):
        """ unpause the settings widgets. They will be re-enabled and the value unfrozen """
        self.setting_controller.resume()
        self.streambar_controller.resume()

    @call_in_wx_main
    def enable_buttons(self, enable):
        """ Enable or disable all the buttons in the button panel """
        for btn in self.pnl_buttons.GetChildren():
            btn.Enable(enable)

    def _cancel_future(self, _):
        """ Cancel the future if it's there and running """
        if self.current_future is not None and not self.current_future.cancelled(
        ):
            if self.current_future.cancel():
                logging.debug("Future cancelled")
            else:
                logging.debug("Failed to cancel future")

    @call_in_wx_main
    def _on_future_done(self, _):
        """ When the future finishes, reset the progress bar and enable the buttons """
        self.gauge_progress.SetValue(0)
        self.lbl_gauge.SetLabel("")
        self.btn_cancel.Disable()
        self.enable_buttons(True)

    def on_close(self, btnid, _):
        logging.debug("Closing window")
        self.streambar_controller.clear()
        self.EndModal(btnid)

    @call_in_wx_main
    def Close(self, *args, **kwargs):
        """
        Request to close the window.
        Make sure to call .Destroy() when not using the dialog anymore.
        """
        # save the return code, as Close() automatically sets it to wx.ID_CANCEL
        # but we want to keep the value potentially set by the button.
        rc = self.ReturnCode
        logging.debug("Closing acquisition dialog")
        super(AcquisitionDialog, self).Close(*args, **kwargs)
        self.ReturnCode = rc
        logging.debug("Dialog closed")

    @call_in_wx_main
    def EndModal(self, retCode):
        """
        Request to close the window, and pass a specific return code.
        Make sure to call .Destroy() when not using the dialog anymore.
        retCode (int)
        """
        super(AcquisitionDialog, self).EndModal(retCode)

    @call_in_wx_main
    def Destroy(self, *args, **kwargs):
        """
        Discards entirely the dialog. It's free'd from the memory.
        Note: in most cases, if it's still opened, it get closed, but it seems
        that with some version of wxPython, it causes a crash, so better call
        Close() first.
        """
        self.streambar_controller.clear()
        logging.debug("Destroying acquisition dialog")
        super(AcquisitionDialog, self).Destroy(*args, **kwargs)
        logging.debug("Dialog destroyed")
Exemplo n.º 9
0
class AcquisitionDialog(xrcfr_plugin):
    def __init__(self, plugin, title, text=None):
        """
        Creates a modal window. The return code is the button number that was
          last pressed before closing the window.
        title (str): The title of the window
        text (None or str): If provided, it is displayed at the top of the window
        """

        super(AcquisitionDialog, self).__init__(plugin.main_app.main_frame)

        self.plugin = plugin

        self.SetTitle(title)

        if text is not None:
            self.lbl_description = AutoWrapStaticText(self.pnl_desc, "")
            self.lbl_description.SetBackgroundColour(self.pnl_desc.GetBackgroundColour())
            self.pnl_desc.GetSizer().Add(self.lbl_description, flag=wx.EXPAND | wx.ALL, border=10)
            self.lbl_description.SetLabel(text)

        self._acq_future_connector = None
        self.buttons = []  # The buttons
        self.current_future = None
        self.btn_cancel.Bind(wx.EVT_BUTTON, self._cancel_future)

        self.setting_controller = SettingsController(self.fp_settings,
                                                     "No settings defined")

        # Create a minimal model for use in the streambar controller

        self._dmodel = MicroscopyGUIData(plugin.main_app.main_data)
        self.hidden_view = StreamView("Plugin View Hidden")
        self.view = MicroscopeView("Plugin View left")
        self.viewport_l.setView(self.view, self._dmodel)
        self.view_r = MicroscopeView("Plugin View right")
        self.viewport_r.setView(self.view_r, self._dmodel)
        self.spectrum_view = MicroscopeView("Plugin View spectrum")
        self.spectrum_viewport.setView(self.spectrum_view, self._dmodel)
        self._dmodel.focussedView.value = self.view
        self._dmodel.views.value = [self.view, self.view_r,
                                    self.spectrum_view]
        self._viewports = (self.viewport_l, self.viewport_r, self.spectrum_viewport)

        self.streambar_controller = StreamBarController(
            self._dmodel,
            self.pnl_streams,
            ignore_view=True
        )

        self.Fit()

    @call_in_wx_main
    def addSettings(self, objWithVA, conf=None):
        """
        Adds settings as one widget on a line for each VigilantAttribute (VA) in
         the given object. Each setting entry created is added to setting_controller.entries.
        objWithVA (object): an object with VAs.

        conf (None or dict of str->config): allows to override the automatic
          selection of the VA widget. See odemis.gui.conf.data for documentation.

        raise:
            LookupError: if no VA is found on the objWithVA

        """
        vas = getVAs(objWithVA)
        if not vas:
            raise LookupError("No VAs found!")

        if not conf:
            conf = {}
        vas_names = util.sorted_according_to(vas.keys(), conf.keys())

        for name in vas_names:
            va = vas[name]
            self.setting_controller.add_setting_entry(name, va, None,
                                                      conf=conf.get(name, None))

        self.Layout()

    @call_in_wx_main
    def addButton(self, label, callback=None, face_colour='def'):
        """
        Add a button at the bottom right of the window. If some buttons are already
        present, they are shifted to the left.

        label (str): text on the button,
        callback (None or callable): the function to be called when the button
          is pressed (with the dialog as argument). If callback is None,
          pressing the button will close the window and the button number will
          be the return code of the dialog.

        """
        btnid = len(self.buttons)
        btn = ImageTextButton(self.pnl_buttons, label=label, height=48,
                              style=wx.ALIGN_CENTER, face_colour=face_colour)
        self.buttons.append(btn)
        sizer = self.pnl_buttons.GetSizer()
        sizer.Add(btn, proportion=1, flag=wx.ALL | wx.ALIGN_RIGHT, border=10)

        if callback is not None and callable(callback):
            # Wrap the callback, to run in a separate thread, so it doesn't block
            # the GUI.
            def button_callback_wrapper(evt, btnid=btnid):
                # TODO: disable the button while the callback is running, so that
                # it's not possible for the user to press twice in a row and cause
                # the code to run twice simultaneously (without very explicitly
                # allowing that).
                try:
                    self.SetReturnCode(btnid)
                    t = threading.Thread(target=callback, args=(self,),
                                         name="Callback for button %s" % (label,))
                    t.start()
                except Exception:
                    logging.exception("Error when processing button %s of plugin %s",
                                      label, self.plugin)
            btn.Bind(wx.EVT_BUTTON, button_callback_wrapper)
        else:
            btn.Bind(wx.EVT_BUTTON, partial(self.on_close, btnid))

        self.pnl_buttons.Layout()

    @call_in_wx_main
    def addStream(self, stream, index=0):
        """
        Adds a stream to the viewport, and a stream entry to the stream panel.
        It also ensures the panel box and viewport are shown.

        Note: If this method is not called, the stream panel and viewports are hidden.

        stream (Stream or None): Stream to be added. Use None to force a viewport
          to be seen without adding a stream.
        index (0, 1, 2, or None): Index of the viewport to add the stream. 0 = left,
          1 = right, 2 = spectrum viewport. If None, it will not show the stream
          on any viewport (and it will be added to the .hidden_view)
        """
        need_layout = False

        if index is None:
            v = self.hidden_view
        else:
            viewport = self._viewports[index]
            v = self._dmodel.views.value[index]
            assert viewport.view is v

            if not viewport.IsShown():
                viewport.Show()
                need_layout = True

        if stream:
            if not self.fp_streams.IsShown():
                self.fp_streams.Show()
                need_layout = True
            self.streambar_controller.addStream(stream, add_to_view=v)

        if need_layout:
            self.Layout()
            self.Update()

    @call_in_wx_main
    def showProgress(self, future):
        """
        Shows a progress bar, based on the status of the progressive future given.
        As long as the future is not finished, the buttons are disabled.

        future (None or Future): The progressive future to show the progress with
          the progress bar. If future is None, it will hide the progress bar.
          If future is cancellable, show a cancel button next to the progress bar.

        """
        if future is not None and not future.cancelled():
            self.current_future = future
            self.enable_buttons(False)

        self.pnl_gauge.Show(future is not None)
        self.Layout()

        if self.current_future is None:
            self._acq_future_connector = None
            return
        else:
            if hasattr(self.current_future, "add_update_callback"):
                self._acq_future_connector = ProgressiveFutureConnector(self.current_future,
                                                                        self.gauge_progress,
                                                                        self.lbl_gauge)
            else:
                # TODO: just pulse the gauge at a "good" frequency (need to use a timer)
                self.gauge_progress.Pulse()

            # If the future is cancellable (ie, has task_canceller), allow to
            # press the "cancel" button, which will call cancel() on the future.
            # TODO: if there is already a "cancel" button in the window, use it
            # instead a providing another one.
            if hasattr(self.current_future, 'task_canceller'):
                self.btn_cancel.Enable()
            else:
                self.btn_cancel.Disable()

        future.add_done_callback(self._on_future_done)

    @call_in_wx_main
    def setAcquisitionInfo(self, text=None, lvl=logging.INFO):
        """
        Displays acquisition info above progress bar.
        text (str or None): text to be displayed. If None is passed, the acquisition
        label will be hidden, so no empty space is displayed.
        lvl (int, from logging.*): log level, which selects the display colour.
        Options: logging.INFO, logging.WARNING, logging.ERROR
        """
        if text is None:
            self.pnl_info.Hide()
        else:
            self.lbl_acquisition_info.SetLabel(text)
            if lvl >= logging.ERROR:
                self.lbl_acquisition_info.SetForegroundColour(FG_COLOUR_ERROR)
            elif lvl >= logging.WARNING:
                self.lbl_acquisition_info.SetForegroundColour(FG_COLOUR_WARNING)
            else:
                self.lbl_acquisition_info.SetForegroundColour(FG_COLOUR_MAIN)
            self.pnl_info.Show()

        self.Layout()

    def pauseSettings(self):
        """ Pause the settings widgets. They will be disabled and the value frozen even when the VAs are changed """
        self.setting_controller.pause()
        self.setting_controller.enable(False)

        self.streambar_controller.pause()
        self.streambar_controller.enable(False)

    def resumeSettings(self):
        """ unpause the settings widgets. They will be re-enabled and the value unfrozen """
        self.setting_controller.enable(True)
        self.setting_controller.resume()

        self.streambar_controller.enable(True)
        self.streambar_controller.resume()

    @call_in_wx_main
    def enable_buttons(self, enable):
        """ Enable or disable all the buttons in the button panel """
        for btn in self.pnl_buttons.GetChildren():
            btn.Enable(enable)

    def _cancel_future(self, _):
        """ Cancel the future if it's there and running """
        if self.current_future is not None and not self.current_future.cancelled():
            if self.current_future.cancel():
                logging.debug("Future cancelled")
            else:
                logging.debug("Failed to cancel future")

    @call_in_wx_main
    def _on_future_done(self, _):
        """ When the future finishes, reset the progress bar and enable the buttons """
        self.gauge_progress.SetValue(0)
        self.lbl_gauge.SetLabel("")
        self.btn_cancel.Disable()
        self.enable_buttons(True)

    def on_close(self, btnid, _):
        logging.debug("Closing window")
        self.streambar_controller.clear()
        self.EndModal(btnid)

    @call_in_wx_main
    def Close(self, *args, **kwargs):
        """
        Request to close the window.
        Make sure to call .Destroy() when not using the dialog anymore.
        """
        # save the return code, as Close() automatically sets it to wx.ID_CANCEL
        # but we want to keep the value potentially set by the button.
        rc = self.ReturnCode
        logging.debug("Closing acquisition dialog")
        super(AcquisitionDialog, self).Close(*args, **kwargs)
        self.ReturnCode = rc
        logging.debug("Dialog closed")

    @call_in_wx_main
    def EndModal(self, retCode):
        """
        Request to close the window, and pass a specific return code.
        Make sure to call .Destroy() when not using the dialog anymore.
        retCode (int)
        """
        super(AcquisitionDialog, self).EndModal(retCode)

    @call_in_wx_main
    def Destroy(self, *args, **kwargs):
        """
        Discards entirely the dialog. It's free'd from the memory.
        Note: in most cases, if it's still opened, it get closed, but it seems
        that with some version of wxPython, it causes a crash, so better call
        Close() first.
        """
        self.streambar_controller.clear()
        logging.debug("Destroying acquisition dialog")
        super(AcquisitionDialog, self).Destroy(*args, **kwargs)
        logging.debug("Dialog destroyed")
Exemplo n.º 10
0
    def test_grrr(self):

        sc = SettingsController(self.frame.fpb.GetChildren()[0],
                                "Settings test")