Exemple #1
0
class ConsoleInitThread(QObject):
    initialized = Signal(object, object)

    def __init__(self, *args, **kwargs):
        super(ConsoleInitThread, self).__init__(*args, **kwargs)
        self.mutex = QMutex()
        self.wait_condition = QWaitCondition()

    def run(self):
        self.mutex.lock()
        kernel_manager = QtKernelManager(kernel_name='python3')
        kernel_manager.start_kernel()

        kernel_client = kernel_manager.client()
        kernel_client.start_channels()

        # notify to update ui
        self.initialized.emit(kernel_manager, kernel_client)

        # wait for exit
        self.wait_condition.wait(self.mutex)
        self.mutex.unlock()

        # stop channels and kernel
        kernel_client.stop_channels()
        kernel_manager.shutdown_kernel(
            now=True)  # add now=True; Fix exit error;  200924 liugang

    def stop(self):
        self.wait_condition.wakeAll()
    class PMGThreadWorker(QObject):
        """
        利用mutex加锁

        """
        signal_print = Signal(str)

        def __init__(self, worker_id: int):
            super(PMGThreadWorker, self).__init__()
            self.quit = False
            self.id = worker_id
            self.mutex = QMutex()
            self.wait_condition = QWaitCondition()

        def work(self):
            i = 0
            while (1):
                self.mutex.lock()
                print(self.thread())
                self.wait_condition.wait(self.mutex)
                for i in range(3):
                    self.signal_print.emit(str(self.id) + ',' + str(i + 1))
                i += 1
                if self.quit:
                    break
                self.mutex.unlock()

        def on_exit(self):
            self.quit = True
            self.wait_condition.wakeAll()

        def wake_all(self):
            self.wait_condition.wakeAll()
Exemple #3
0
class BaseThread(QThread, metaclass=QABCMeta):

    """Base thread of Cython functions."""

    @abstractmethod
    def __init__(self, parent: QWidget) -> None:
        super(BaseThread, self).__init__(parent)
        self.finished.connect(self.deleteLater)
        self.is_stop = False
        self.mutex = QMutex()

    @Slot()
    def stop(self) -> None:
        """Stop the algorithm."""
        self.mutex.unlock()
        self.is_stop = True
        self.mutex.lock()
class ZoomEventBuffer(object):
    """
    A mutex-lockable buffer that stores zoom request events.

    """
    def __init__(self):
        self.buffer = []
        self.mutex = QMutex()

    def _clear(self):
        self.mutex.lock()
        self.buffer = []
        self.mutex.unlock()

    def _put(self, value):
        self.mutex.lock()

        # Don't let the buffer fill up too much.
        # Keep just the most recent zoom events.
        while len(self.buffer) > 5:
            self.buffer.pop()

        self.buffer.insert(0, value)

        self.mutex.unlock()

    def _get(self):
        self.mutex.lock()
        if len(self.buffer) > 0:
            value = self.buffer.pop()
        else:
            value = None
        self.mutex.unlock()
        return value
Exemple #5
0
class UpdateThread(QThread):
    pixmapReady = Signal(QImage)
    stopped = Signal()
    errored = Signal(str)

    def __init__(self, cam_id, image_width, image_height):
        super(UpdateThread, self).__init__()
        self._width = 640
        self._height = 480
        self._stopped = False
        self._update_mutex = QMutex()

        self._cam_id = cam_id
        self._image_width = image_width
        self._image_height = image_height

    @property
    def width(self):
        return self._width

    @width.setter
    def width(self, value):
        self._update_mutex.lock()
        self._width = value
        self._update_mutex.unlock()

    @property
    def height(self):
        return self._height

    @height.setter
    def height(self, value):
        self._update_mutex.lock()
        self._height = value
        self._update_mutex.unlock()

    def stop(self):
        self._update_mutex.lock()
        self._stopped = True
        self._update_mutex.unlock()

    def run(self):
        try:
            import cv2
        except ImportError as e:
            self.errored.emit(str(e))
            self.stopped.emit()
            return
        try:
            ueye = UEye(self._cam_id, self._image_width, self._image_height)
        except CameraException as e:
            self.errored.emit(str(e))
            self.stopped.emit()
            return
        self._ueye.start_video_capture()

        while True:
            try:
                self._update_mutex.lock()
                if self._stopped:
                    ueye.close()
                    del ueye
                    self.stopped.emit()
                    return
            finally:
                self._update_mutex.unlock()

            frame = ueye.get_video_frame()
            if frame is None:
                ueye.close()
                del ueye
                self.stopped.emit()
                return

            rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            h, w, ch = rgb_image.shape
            bytes_per_line = ch * w
            convert_to_qt_format = QImage(
                rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888
            )
            try:
                self._update_mutex.lock()
                p = convert_to_qt_format.scaled(
                    self._width, self._height, Qt.KeepAspectRatio
                )
            finally:
                self._update_mutex.unlock()
            self.pixmapReady.emit(p)
Exemple #6
0
class ExternalShellBase(QWidget):
    """External Shell widget: execute Python script in a separate process"""
    SHELL_CLASS = None
    redirect_stdio = Signal(bool)
    sig_finished = Signal()

    def __init__(self,
                 parent=None,
                 fname=None,
                 wdir=None,
                 history_filename=None,
                 show_icontext=True,
                 light_background=True,
                 menu_actions=None,
                 show_buttons_inside=True,
                 show_elapsed_time=True):
        QWidget.__init__(self, parent)

        self.menu_actions = menu_actions
        self.write_lock = QMutex()
        self.buffer_lock = QMutex()
        self.buffer = []

        self.run_button = None
        self.kill_button = None
        self.options_button = None
        self.icontext_action = None

        self.show_elapsed_time = show_elapsed_time

        self.fname = fname
        if wdir is None:
            wdir = osp.dirname(osp.abspath(fname))
        self.wdir = wdir if osp.isdir(wdir) else None
        self.arguments = ""

        self.shell = self.SHELL_CLASS(parent, get_conf_path(history_filename))
        self.shell.set_light_background(light_background)
        self.shell.execute.connect(self.send_to_process)
        self.shell.sig_keyboard_interrupt.connect(self.keyboard_interrupt)
        # Redirecting some SIGNALs:
        self.shell.redirect_stdio.connect(
            lambda state: self.redirect_stdio.emit(state))

        self.state_label = None
        self.time_label = None

        vlayout = QVBoxLayout()
        toolbar_buttons = self.get_toolbar_buttons()
        if show_buttons_inside:
            self.state_label = QLabel()
            hlayout = QHBoxLayout()
            hlayout.addWidget(self.state_label)
            hlayout.addStretch(0)
            hlayout.addWidget(self.create_time_label())
            hlayout.addStretch(0)
            for button in toolbar_buttons:
                hlayout.addWidget(button)
            vlayout.addLayout(hlayout)
        else:
            vlayout.setContentsMargins(0, 0, 0, 0)
        vlayout.addWidget(self.get_shell_widget())
        self.setLayout(vlayout)
        self.resize(640, 480)
        if parent is None:
            self.setWindowIcon(self.get_icon())
            self.setWindowTitle(_("Console"))

        self.t0 = None
        self.timer = QTimer(self)

        self.process = None

        self.is_closing = False

        if show_buttons_inside:
            self.update_time_label_visibility()

    @Slot(bool)
    def set_elapsed_time_visible(self, state):
        self.show_elapsed_time = state
        if self.time_label is not None:
            self.time_label.setVisible(state)

    def create_time_label(self):
        """Create elapsed time label widget (if necessary) and return it"""
        if self.time_label is None:
            self.time_label = QLabel()
        return self.time_label

    def update_time_label_visibility(self):
        self.time_label.setVisible(self.show_elapsed_time)

    def is_running(self):
        if self.process is not None:
            return self.process.state() == QProcess.Running

    def get_toolbar_buttons(self):
        if self.run_button is None:
            self.run_button = create_toolbutton(
                self,
                text=_("Run"),
                icon=ima.icon('run'),
                tip=_("Run again this program"),
                triggered=self.start_shell)
        if self.kill_button is None:
            self.kill_button = create_toolbutton(
                self,
                text=_("Kill"),
                icon=ima.icon('kill'),
                tip=_("Kills the current process, "
                      "causing it to exit immediately"))
        buttons = [self.run_button]
        if self.options_button is None:
            options = self.get_options_menu()
            if options:
                self.options_button = create_toolbutton(
                    self, text=_('Options'), icon=ima.icon('tooloptions'))
                self.options_button.setPopupMode(QToolButton.InstantPopup)
                menu = QMenu(self)
                add_actions(menu, options)
                self.options_button.setMenu(menu)
        if self.options_button is not None:
            buttons.append(self.options_button)
        buttons.append(self.kill_button)
        return buttons

    def set_icontext_visible(self, state):
        """Set icon text visibility"""
        for widget in self.get_toolbar_buttons():
            if state:
                widget.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
            else:
                widget.setToolButtonStyle(Qt.ToolButtonIconOnly)

    def get_options_menu(self):
        self.show_time_action = create_action(
            self,
            _("Show elapsed time"),
            toggled=self.set_elapsed_time_visible)
        self.show_time_action.setChecked(self.show_elapsed_time)
        actions = [self.show_time_action]
        if self.menu_actions is not None:
            actions += [None] + self.menu_actions
        return actions

    def get_shell_widget(self):
        return self.shell

    def get_icon(self):
        raise NotImplementedError

    def show_time(self, end=False):
        if self.time_label is None:
            return
        elapsed_time = time() - self.t0
        if elapsed_time > 24 * 3600:  # More than a day...!
            format = "%d %H:%M:%S"
        else:
            format = "%H:%M:%S"
        if end:
            color = "#AAAAAA"
        else:
            color = "#AA6655"
        text = "<span style=\'color: %s\'><b>%s" \
               "</b></span>" % (color, strftime(format, gmtime(elapsed_time)))
        self.time_label.setText(text)

    def closeEvent(self, event):
        if self.process is not None:
            self.is_closing = True
            self.process.kill()
            self.process.waitForFinished(100)

        try:
            self.timer.timeout.disconnect(self.show_time)
        except (RuntimeError, TypeError):
            pass

    def set_running_state(self, state=True):
        self.set_buttons_runnning_state(state)
        self.shell.setReadOnly(not state)
        if state:
            if self.state_label is not None:
                self.state_label.setText(
                    _("<span style=\'color: #44AA44\'><b>Running...</b></span>"
                      ))
            self.t0 = time()
            self.timer.timeout.connect(self.show_time)
            self.timer.start(1000)
        else:
            if self.state_label is not None:
                self.state_label.setText(_('Terminated.'))
            try:
                self.timer.timeout.disconnect(self.show_time)
            except (RuntimeError, TypeError):
                pass

    def set_buttons_runnning_state(self, state):
        self.run_button.setVisible(not state)
        self.kill_button.setVisible(state)

    @Slot(bool)
    def start_shell(self, ask_for_arguments=False):
        """Start shell"""
        if ask_for_arguments and not self.get_arguments():
            self.set_running_state(False)
            return
        try:
            self.terminate_button.clicked.disconnect(self.process.terminate)
            self.kill_button.clicked.disconnect(self.process.terminate)
        except (AttributeError, RuntimeError, TypeError):
            pass
        self.create_process()

    @Slot()
    def get_arguments(self):
        arguments, valid = QInputDialog.getText(self, _('Arguments'),
                                                _('Command line arguments:'),
                                                QLineEdit.Normal,
                                                self.arguments)
        if valid:
            self.arguments = to_text_string(arguments)
        return valid

    def create_process(self):
        raise NotImplementedError

    def finished(self, exit_code, exit_status):
        self.shell.flush()
        self.sig_finished.emit()
        if self.is_closing:
            return
        self.set_running_state(False)
        self.show_time(end=True)

#===============================================================================
#    Input/Output
#===============================================================================

    def transcode(self, qba):
        try:
            return to_text_string(qba.data(), 'utf8')
        except UnicodeDecodeError:
            return qba.data()

    def get_stdout(self):
        self.process.setReadChannel(QProcess.StandardOutput)
        qba = QByteArray()
        while self.process.bytesAvailable():
            qba += self.process.readAllStandardOutput()
        return self.transcode(qba)

    def get_stderr(self):
        self.process.setReadChannel(QProcess.StandardError)
        qba = QByteArray()
        while self.process.bytesAvailable():
            qba += self.process.readAllStandardError()
        return self.transcode(qba)

    def write_output(self):
        # if we are already writing something else,
        # store the present message in a buffer
        if not self.write_lock.tryLock():
            self.buffer_lock.lock()
            self.buffer.append(self.get_stdout())
            self.buffer_lock.unlock()

            if not self.write_lock.tryLock():
                return

        self.shell.write(self.get_stdout(), flush=True)

        while True:
            self.buffer_lock.lock()
            messages = self.buffer
            self.buffer = []

            if not messages:
                self.write_lock.unlock()
                self.buffer_lock.unlock()
                return

            self.buffer_lock.unlock()
            self.shell.write("\n".join(messages), flush=True)

    def send_to_process(self, qstr):
        raise NotImplementedError

    def send_ctrl_to_process(self, letter):
        char = chr("abcdefghijklmnopqrstuvwxyz".index(letter) + 1)
        byte_array = QByteArray()
        byte_array.append(char)
        self.process.write(byte_array)
        self.process.waitForBytesWritten(-1)
        self.shell.write(LOCALE_CODEC.toUnicode(byte_array), flush=True)

    def keyboard_interrupt(self):
        raise NotImplementedError
Exemple #7
0
class ExternalShellBase(QWidget):
    """External Shell widget: execute Python script in a separate process"""
    SHELL_CLASS = None
    redirect_stdio = Signal(bool)
    sig_finished = Signal()
    
    def __init__(self, parent=None, fname=None, wdir=None,
                 history_filename=None, show_icontext=True,
                 light_background=True, menu_actions=None,
                 show_buttons_inside=True, show_elapsed_time=True):
        QWidget.__init__(self, parent)
        
        self.menu_actions = menu_actions
        self.write_lock = QMutex()
        self.buffer_lock = QMutex()
        self.buffer = []
        
        self.run_button = None
        self.kill_button = None
        self.options_button = None
        self.icontext_action = None

        self.show_elapsed_time = show_elapsed_time
        
        self.fname = fname
        if wdir is None:
            wdir = osp.dirname(osp.abspath(fname))
        self.wdir = wdir if osp.isdir(wdir) else None
        self.arguments = ""
        
        self.shell = self.SHELL_CLASS(parent, get_conf_path(history_filename))
        self.shell.set_light_background(light_background)
        self.shell.execute.connect(self.send_to_process)
        self.shell.sig_keyboard_interrupt.connect(self.keyboard_interrupt)
        # Redirecting some SIGNALs:
        self.shell.redirect_stdio.connect(
                     lambda state: self.redirect_stdio.emit(state))
        
        self.state_label = None
        self.time_label = None
                
        vlayout = QVBoxLayout()
        toolbar_buttons = self.get_toolbar_buttons()
        if show_buttons_inside:
            self.state_label = QLabel()
            hlayout = QHBoxLayout()
            hlayout.addWidget(self.state_label)
            hlayout.addStretch(0)
            hlayout.addWidget(self.create_time_label())
            hlayout.addStretch(0)
            for button in toolbar_buttons:
                hlayout.addWidget(button)
            vlayout.addLayout(hlayout)
        else:
            vlayout.setContentsMargins(0, 0, 0, 0)
        vlayout.addWidget(self.get_shell_widget())
        self.setLayout(vlayout)
        self.resize(640, 480)
        if parent is None:
            self.setWindowIcon(self.get_icon())
            self.setWindowTitle(_("Console"))

        self.t0 = None
        self.timer = QTimer(self)

        self.process = None
        
        self.is_closing = False

        if show_buttons_inside:
            self.update_time_label_visibility()

    @Slot(bool)
    def set_elapsed_time_visible(self, state):
        self.show_elapsed_time = state
        if self.time_label is not None:
            self.time_label.setVisible(state)
            
    def create_time_label(self):
        """Create elapsed time label widget (if necessary) and return it"""
        if self.time_label is None:
            self.time_label = QLabel()
        return self.time_label
    
    def update_time_label_visibility(self):
        self.time_label.setVisible(self.show_elapsed_time)
        
    def is_running(self):
        if self.process is not None:
            return self.process.state() == QProcess.Running
        
    def get_toolbar_buttons(self):
        if self.run_button is None:
            self.run_button = create_toolbutton(self, text=_("Run"),
                                             icon=ima.icon('run'),
                                             tip=_("Run again this program"),
                                             triggered=self.start_shell)
        if self.kill_button is None:
            self.kill_button = create_toolbutton(self, text=_("Kill"),
                                     icon=ima.icon('kill'),
                                     tip=_("Kills the current process, "
                                           "causing it to exit immediately"))
        buttons = [self.run_button]
        if self.options_button is None:
            options = self.get_options_menu()
            if options:
                self.options_button = create_toolbutton(self, text=_('Options'),
                                            icon=ima.icon('tooloptions'))
                self.options_button.setPopupMode(QToolButton.InstantPopup)
                menu = QMenu(self)
                add_actions(menu, options)
                self.options_button.setMenu(menu)
        if self.options_button is not None:
            buttons.append(self.options_button)
        buttons.append(self.kill_button)
        return buttons
            
    def set_icontext_visible(self, state):
        """Set icon text visibility"""
        for widget in self.get_toolbar_buttons():
            if state:
                widget.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
            else:
                widget.setToolButtonStyle(Qt.ToolButtonIconOnly)
    
    def get_options_menu(self):
        self.show_time_action = create_action(self, _("Show elapsed time"),
                                          toggled=self.set_elapsed_time_visible)
        self.show_time_action.setChecked(self.show_elapsed_time)
        actions = [self.show_time_action]
        if self.menu_actions is not None:
            actions += [None]+self.menu_actions
        return actions
    
    def get_shell_widget(self):
        return self.shell
    
    def get_icon(self):
        raise NotImplementedError
        
    def show_time(self, end=False):
        if self.time_label is None:
            return
        elapsed_time = time()-self.t0
        if elapsed_time > 24*3600: # More than a day...!
            format = "%d %H:%M:%S"
        else:
            format = "%H:%M:%S"
        if end:
            color = "#AAAAAA"
        else:
            color = "#AA6655"
        text = "<span style=\'color: %s\'><b>%s" \
               "</b></span>" % (color, strftime(format, gmtime(elapsed_time)))
        self.time_label.setText(text)
        
    def closeEvent(self, event):
        if self.process is not None:
            self.is_closing = True
            self.process.kill()
            self.process.waitForFinished(100)

        try:
            self.timer.timeout.disconnect(self.show_time)
        except (RuntimeError, TypeError):
            pass
    
    def set_running_state(self, state=True):
        self.set_buttons_runnning_state(state)
        self.shell.setReadOnly(not state)
        if state:
            if self.state_label is not None:
                self.state_label.setText(_(
                   "<span style=\'color: #44AA44\'><b>Running...</b></span>"))
            self.t0 = time()
            self.timer.timeout.connect(self.show_time)
            self.timer.start(1000)        
        else:
            if self.state_label is not None:
                self.state_label.setText(_('Terminated.'))
            try:
                self.timer.timeout.disconnect(self.show_time)
            except (RuntimeError, TypeError):
                pass

    def set_buttons_runnning_state(self, state):
        self.run_button.setVisible(not state and not self.is_ipykernel)
        self.kill_button.setVisible(state)

    @Slot(bool)
    def start_shell(self, ask_for_arguments=False):
        """Start shell"""
        if ask_for_arguments and not self.get_arguments():
            self.set_running_state(False)
            return
        try:
            self.terminate_button.clicked.disconnect(self.process.terminate)
            self.kill_button.clicked.disconnect(self.process.terminate)
        except (AttributeError, RuntimeError, TypeError):
            pass
        self.create_process()

    @Slot()
    def get_arguments(self):
        arguments, valid = QInputDialog.getText(self, _('Arguments'),
                                                _('Command line arguments:'),
                                                QLineEdit.Normal,
                                                self.arguments)
        if valid:
            self.arguments = to_text_string(arguments)
        return valid
    
    def create_process(self):
        raise NotImplementedError
    
    def finished(self, exit_code, exit_status):
        self.shell.flush()
        self.sig_finished.emit()
        if self.is_closing:
            return
        self.set_running_state(False)
        self.show_time(end=True)
    
#===============================================================================
#    Input/Output
#===============================================================================
    def transcode(self, qba):
        try:
            return to_text_string(qba.data(), 'utf8')
        except UnicodeDecodeError:
            return qba.data()
    
    def get_stdout(self):
        self.process.setReadChannel(QProcess.StandardOutput)
        qba = QByteArray()
        while self.process.bytesAvailable():
            qba += self.process.readAllStandardOutput()
        return self.transcode(qba)
    
    def get_stderr(self):
        self.process.setReadChannel(QProcess.StandardError)
        qba = QByteArray()
        while self.process.bytesAvailable():
            qba += self.process.readAllStandardError()
        return self.transcode(qba)

    def write_output(self):
        # if we are already writing something else,
        # store the present message in a buffer
        if not self.write_lock.tryLock():
            self.buffer_lock.lock()
            self.buffer.append(self.get_stdout())
            self.buffer_lock.unlock()

            if not self.write_lock.tryLock():
                return

        self.shell.write(self.get_stdout(), flush=True)

        while True:
            self.buffer_lock.lock()
            messages = self.buffer
            self.buffer = []

            if not messages:
                self.write_lock.unlock()
                self.buffer_lock.unlock()
                return

            self.buffer_lock.unlock()
            self.shell.write("\n".join(messages), flush=True)

    def send_to_process(self, qstr):
        raise NotImplementedError

    def send_ctrl_to_process(self, letter):
        char = chr("abcdefghijklmnopqrstuvwxyz".index(letter) + 1)
        byte_array = QByteArray()
        byte_array.append(char)
        self.process.write(byte_array)
        self.process.waitForBytesWritten(-1)
        self.shell.write(LOCALE_CODEC.toUnicode(byte_array), flush=True)

    def keyboard_interrupt(self):
        raise NotImplementedError
Exemple #8
0
class BipapThread(QObject):
    signal = Signal(str)
    def __init__(self, serl, codegen, que):
        self.pressureque = que
        self.serl = serl
        self.codegen = codegen
        self.codegen.GenerateCMV()
        self.codelist = self.codegen.gcodestr.splitlines()
        self.linecount = len(self.codelist)
        self.flagStop = False
        self.pause = True
        self.gcode_exec_state = GcodeStates.READY_TO_SEND
        self.gcode_move_count = 0
        self.presentPosition = (0,0)
        self.Tic = 0
        self.Toc = 0
        self.xyIncr = self.codegen.Dt
        self.gstr = ""
        self.sremsg = ""
        self.serialmutex = QMutex()
        self.startdelay = -1
        super().__init__()

    def gcodestep(self):
        self.gstr = "G01 X" + str(self.xyIncr) + " Y" + str(self.xyIncr) + " F1000\r\n"
        if self.xyIncr < self.codegen.xmax:
            self.xyIncr += 1

    def Stop(self):
        self.flagStop = True

    def updateGcode(self, codegen):
        self.codegen = codegen
        self.codegen.GenerateCMV()
        self.codelist = self.codegen.gcodestr.splitlines()

    def StartMoving(self):
        self.pause = False

    def StartMovingAfter(self, delay):
        self.startdelay = delay

    def StopMoving(self):
        self.pause = True
        self.xyIncr = self.codegen.Dt

    @Slot()
    def run(self):
        lst = []
        while 1:
            if self.flagStop:
                break
            try:
                if not self.pause:
                    if self.gcode_exec_state == GcodeStates.READY_TO_SEND:
                        self.gcodestep()
                        self.serialmutex.lock()
                        self.serl.write(self.gstr.encode("utf-8"))
                        self.serialmutex.unlock()
                        self.gcode_move_count += 1
                        if self.gcode_move_count >= 130:
                            #self.pause = True
                            self.gcode_move_count = 0
                        else:
                            self.gcode_exec_state = GcodeStates.WAIT_FOR_TIMEOUT
                            self.Tic = time.perf_counter()
                    if self.gcode_exec_state == GcodeStates.WAIT_FOR_TIMEOUT:
                        if (time.perf_counter() - self.Tic) >= 0.15:
                            #print("Gcode Executed\r\n")
                            self.gcode_exec_state = GcodeStates.READY_TO_SEND
                elif self.startdelay > 0:
                    time.sleep(self.startdelay)
                    self.startdelay = -1
                    self.pause = False
            except serial.SerialException as ex:
                print("Error In SerialException" + str(ex.strerror))
class SegmentationThread(QThread):
    """
    Method to run calculation task in separated Thread. This allows to not freeze main window.
    To get info if calculation is done connect to :py:meth:`~.QThread.finished`.
    """

    execution_done = Signal(SegmentationResult)
    """
    Signal contains result of segmentation algorithm. Emitted if calculation ends without exception and
    :py:meth:`SegmentationAlgorithm.calculation_run` return not None result.
    """
    progress_signal = Signal(str, int)
    """
    Signal with information about progress. This is proxy for :py:meth:`SegmentationAlgorithm.calculation_run`
    `report_fun` parameter`
    """
    info_signal = Signal(str)
    exception_occurred = Signal(Exception)
    """Signal emitted when some exception occur during calculation. """
    def __init__(self, algorithm: SegmentationAlgorithm):
        super().__init__()
        self.finished.connect(self.finished_task)
        self.algorithm = algorithm
        self.clean_later = False
        self.cache = None
        self.mutex = QMutex()
        self.rerun = False, QThread.InheritPriority

    def get_info_text(self):
        """Proxy for :py:meth:`.SegmentationAlgorithm.get_info_text`."""
        return self.algorithm.get_info_text()

    def send_info(self, text, num):
        self.progress_signal.emit(text, num)

    def run(self):
        """the calculation are done here"""
        if self.algorithm.image is None:
            # assertion for running algorithm without image
            print(f"No image in class {self.algorithm.__class__}",
                  file=sys.stderr)
            return
        try:
            segment_data = self.algorithm.calculation_run_wrap(self.send_info)
        except Exception as e:
            self.exception_occurred.emit(e)
            return
        if segment_data is None:
            return
        self.execution_done.emit(segment_data)

    def finished_task(self):
        """
        Called on calculation finished. Check if cache is not empty.
        In such case start calculation again with new parameters.
        """
        self.mutex.lock()
        if self.cache is not None:
            args, kwargs = self.cache
            self.algorithm.set_parameters(*args, **kwargs)
            self.cache = None
            self.clean_later = False
        if self.rerun[0]:
            self.rerun = False, QThread.InheritPriority
            super().start(self.rerun[1])
        elif self.clean_later:
            self.algorithm.clean()
            self.clean_later = False
        self.mutex.unlock()

    def clean(self):
        """
        clean cache if thread is running. Call :py:meth:`SegmentationAlgorithm.clean` otherwise. :
        """
        self.mutex.lock()
        if self.isRunning():
            self.clean_later = True
        else:
            self.algorithm.clean()
        self.mutex.unlock()

    def set_parameters(self, *args, **kwargs):
        """
        check if calculation is running.
        If yes then cache parameters until it finish, otherwise call :py:meth:`.SegmentationAlgorithm.set_parameters`
        """
        self.mutex.lock()
        if self.isRunning():
            self.cache = args, kwargs
            self.clean_later = False
        else:
            self.algorithm.set_parameters(*args, **kwargs)
        self.mutex.unlock()

    def start(self, priority: "QThread.Priority" = QThread.InheritPriority):
        """
        If calculation is running remember to restart it with new parameters.

        Otherwise start immediately.
        """
        self.mutex.lock()
        if self.isRunning():
            self.clean_later = False
            self.rerun = True, priority
        else:
            super().start(priority)
        self.mutex.unlock()