示例#1
0
class InitializerHelper(QtCore.QObject):

    initializing = QtCore.Signal(object)
    initialized = QtCore.Signal(object)
    exception = QtCore.Signal(object, object)
    finished = QtCore.Signal(float)

    def __init__(self, drivers, register_finalizer, parallel, dependencies):
        super().__init__()
        self.drivers = drivers
        self.register_finalizer = register_finalizer
        self.parallel = parallel
        self.dependencies = dependencies

    def process(self):
        start = time.time()
        initialize_many(drivers=self.drivers,
                        register_finalizer=self.register_finalizer,
                        on_initializing=self.on_initializing,
                        on_initialized=self.on_initialized,
                        on_exception=self.on_exception,
                        concurrent=self.parallel,
                        dependencies=self.dependencies)
        self.finished.emit(time.time() - start)

    def on_initializing(self, driver):
        self.initializing.emit(driver)

    def on_initialized(self, driver):
        self.initialized.emit(driver)

    def on_exception(self, driver, ex):
        self.exception.emit(driver, ex)
示例#2
0
class FeatScan(Scan):
    """A backend to scan a feat for a given instrument.
    """

    #: Signal emitted before starting a new iteration
    #: Parameters: loop counter, step value, overrun
    iteration = QtCore.Signal(int, int, bool)

    #: Signal emitted when the loop finished.
    #: The parameter is used to inform if the loop was canceled.
    loop_done = QtCore.Signal(bool)

    instrument = InstrumentSlot

    #: Name of the scanned feat
    #: :type: str

    def __init__(self, feat_name, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.feat_name = feat_name

    def _pre_body(self, counter, new_value, overrun):
        setattr(self.instrument, self.feat_name, new_value)

    @property
    def feat_units(self):
        """Units of the scanned feat.
        """
        target = self.instrument
        feat_name = self.feat_name
        feat = target.feats[feat_name]
        return str(feat.units)
示例#3
0
    def setupUi(self, parent):
        self.resize(275, 172)
        self.setWindowTitle('Convert units')
        self.layout = QtGui.QVBoxLayout(parent)
        self.layout.setSizeConstraint(QtGui.QLayout.SetFixedSize)
        align = (QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing
                 | QtCore.Qt.AlignVCenter)

        self.layout1 = QtGui.QHBoxLayout()
        self.label1 = QtGui.QLabel()
        self.label1.setMinimumSize(QtCore.QSize(100, 0))
        self.label1.setText('Convert from:')
        self.label1.setAlignment(align)

        self.layout1.addWidget(self.label1)
        self.source_units = QtGui.QLineEdit()
        self.source_units.setReadOnly(True)
        self.layout1.addWidget(self.source_units)

        self.layout.addLayout(self.layout1)

        self.layout2 = QtGui.QHBoxLayout()
        self.label2 = QtGui.QLabel()
        self.label2.setMinimumSize(QtCore.QSize(100, 0))
        self.label2.setText('to:')
        self.label2.setAlignment(align)
        self.layout2.addWidget(self.label2)

        self.destination_units = QtGui.QLineEdit()
        self.layout2.addWidget(self.destination_units)

        self.layout.addLayout(self.layout2)

        self.message = QtGui.QLabel()
        self.message.setText('')
        self.message.setAlignment(QtCore.Qt.AlignCenter)

        self.layout.addWidget(self.message)

        self.buttonBox = QtGui.QDialogButtonBox()
        self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
        self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok)
        self.layout.addWidget(self.buttonBox)
        self.buttonBox.setEnabled(False)

        self.buttonBox.accepted.connect(self.accept)
        self.destination_units.textChanged.connect(self.check)

        self.setLayout(self.layout)
        self.destination_units.setFocus()
示例#4
0
class Loop(Backend):

    #: Signal emitted before starting a new iteration
    #: Parameters: loop counter, iterations, overrun
    iteration = QtCore.Signal(int, int, bool)

    #: Signal emitted when the loop finished.
    #: The parameter is used to inform if the loop was canceled.
    loop_done = QtCore.Signal(bool)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.body = None
        self._active = False
        self._internal_func = None

    def stop(self):
        self._active = False

    def start(self, body, interval=0, iterations=0, timeout=0):
        self._active = True
        body = body or self.body

        def internal(counter,
                     overrun=False,
                     schedule=QtCore.QTimer.singleShot):
            if not self._active:
                self.loop_done.emit(True)
                return

            st = time.time()
            self.iteration.emit(counter, iterations, overrun)
            body(counter, iterations, overrun)

            if iterations and counter + 1 == iterations:
                self.loop_done.emit(False)
                return
            elif not self._active:
                self.loop_done.emit(True)
                return

            sleep = interval - (time.time() - st)
            schedule(sleep * 1000 if sleep > 0 else 0,
                     lambda: self._internal_func(counter + 1, sleep < 0))

        self._internal_func = internal
        if timeout:
            QtCore.QTimer.singleShot(timeout * 1000, self.stop)
        QtCore.QTimer.singleShot(0, lambda: self._internal_func(0))
示例#5
0
class AmplitudeScannerUi(Frontend):
    """A frontend for the AmplitudeScanner backend.

    Provides controls to select the scan range and to start and stop de scanning.
    """

    # a declarative way to indicate the user interface file to use.
    # The file must be located next to the python file where this class
    # is defined.
    gui = 'amplitude_scanner.ui'

    # connect widgets to instruments using connect_setup automatically.
    auto_connect = True

    # amplitudes
    request_start = QtCore.Signal(object)

    def _scan(self):
        start, stop, steps = (getattr(self.widget, name).value()
                              for name in ('start', 'stop', 'steps'))
        amplitudes = Q_(np.linspace(start, stop, steps), 'volt')
        self.request_start.emit(amplitudes)

    def connect_backend(self):

        # This method is called after gui has been loaded (referenced in self.widget)
        # and the backend is connected to the frontend (referenced in self.backend).
        # In this case, we use it to connect the scan button, to the scan method
        # and the request_start signal to the _scan_amplitude
        self.widget.scan_amplitudes.clicked.connect(self._scan)
        self.request_start.connect(self.backend._scan_amplitude)
示例#6
0
class AmplitudeScannerShutter(Backend):
    """A complex application that requires a function generator, an
    oscilloscope and a shutter.

    We could subclass AmplitudeScanner, but we have chosen to embed it instead.
    """

    # Enumerate drivers required by the backend marking them with InstrumentSlot
    osci = InstrumentSlot
    fungen = InstrumentSlot
    shutter = InstrumentSlot

    # Embedded apps (Notice we embed the backend, not the Frontend)
    scanner = AmplitudeScanner

    # This signal will be emited when new data is available.
    new_data = QtCore.Signal(object, object)

    def scan_amplitude(self, amplitudes):
        """Open the shutter an then for each amplitude:
        - scan the amplitude of the function generator.
        - sleeps .3 seconds.
        - measures the trace of the oscilloscope.
        - yields amplitude, data for each amplitude.

        Finally, close the shutter.

        :param amplitudes: iterable of amplitudes.
        """
        self.shutter.opened = True

        # We use the embedded app to perform the actual operation.
        # Notice that we have not explicitly instantiated the AmplitudeScanner
        # nor provided the instruments. Lantz has done this in the back
        # connecting InstrumentsSlots by name.
        # i.e. self.osci is self.scanner.osci
        for amplitude, data in self.scanner.scan_amplitude(amplitudes):
            self.new_data.emit(amplitude, data)
            yield amplitude, data

        self.shutter.opened = False

    def _scan_amplitude(self, amplitudes):
        """Because scan_amplitude is a generator, this helper functions is used
        to iterate over all the items.
        """
        return list(self.scan_amplitude(amplitudes))

    def default_scan(self):
        """A linear scan from 1 to 19 Volts.
        """
        return list(self.scan_amplitude(Q_(list(range(1, 20)), 'volt')))
示例#7
0
文件: myapps.py 项目: zhong-lab/code
class LoopOsciMeasure(Backend):
    """An application that measures from an Osci in a loop.
    """

    # Enumerate drivers required by the backend marking them with InstrumentSlot
    osci = InstrumentSlot

    # Embedded apps (Notice we embed the backend, not the Frontend)
    loop = Loop

    # This signal will be emitted when new data is available.
    new_data = QtCore.Signal(object)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.loop.body = self.measure

    def measure(self, counter, iterations, overrun):
        data = self.osci.measure()
        self.new_data.emit(data)
示例#8
0
class LoopUi(Frontend):

    gui = 'loop.ui'

    auto_connect = False

    request_start = QtCore.Signal(object, object, object, object)
    request_stop = QtCore.Signal()

    def connect_backend(self):
        super().connect_backend()

        self.widget.start_stop.clicked.connect(self.on_start_stop_clicked)
        self.widget.mode.currentIndexChanged.connect(self.on_mode_changed)
        self.widget.iterations.valueChanged.connect(self.recalculate)
        self.widget.duration.valueChanged.connect(self.recalculate)
        self.widget.interval.valueChanged.connect(self.recalculate)
        self.widget.progress_bar.setValue(0)

        self._ok_palette = QtGui.QPalette(self.widget.progress_bar.palette())
        self._overrun_palette = QtGui.QPalette(
            self.widget.progress_bar.palette())
        self._overrun_palette.setColor(QtGui.QPalette.Highlight,
                                       QtGui.QColor(QtCore.Qt.red))

        self.backend.iteration.connect(self.on_iteration)
        self.backend.loop_done.connect(self.on_loop_done)

        self.request_start.connect(self.backend.start)
        self.request_stop.connect(self.backend.stop)

    def on_start_stop_clicked(self, value=None):
        if self.backend._active:
            self.widget.start_stop.setText('...')
            self.widget.start_stop.setEnabled(False)
            self.request_stop.emit()
            return

        self.widget.start_stop.setText('Stop')
        self.widget.start_stop.setChecked(True)

        mode = self.widget.mode.currentIndex()
        interval, iterations, duration = [
            getattr(self.widget, name).value()
            for name in 'interval iterations duration'.split()
        ]
        if mode == StopMode.Continuous:
            self.request_start.emit(None, interval, 0, 0)
        elif mode == StopMode.Iterations:
            self.request_start.emit(None, interval, iterations, 0)
        elif mode == StopMode.Duration:
            self.request_start.emit(None, interval, 0, duration)
        elif mode == StopMode.IterationsTimeOut:
            self.request_start.emit(None, interval, iterations, duration)

    def recalculate(self, *args):
        mode = self.widget.mode.currentIndex()
        if mode == StopMode.Duration:
            iterations = self.widget.duration.value(
            ) / self.widget.interval.value()
            self.widget.iterations.setValue(math.ceil(iterations))
        elif mode == StopMode.Iterations:
            self.widget.duration.setValue(self.widget.iterations.value() *
                                          self.widget.interval.value())

    def on_iteration(self, counter, iterations, overrun):
        pbar = self.widget.progress_bar

        if not counter:
            if iterations:
                pbar.setMaximum(iterations + 1)
            else:
                pbar.setMaximum(0)

        if iterations:
            pbar.setValue(counter + 1)

        if overrun:
            pbar.setPalette(self._overrun_palette)
        else:
            pbar.setPalette(self._ok_palette)

    def on_mode_changed(self, new_index):
        if new_index == StopMode.Continuous:
            self.widget.duration.setEnabled(False)
            self.widget.iterations.setEnabled(False)
        elif new_index == StopMode.Duration:
            self.widget.duration.setEnabled(True)
            self.widget.iterations.setEnabled(True)
            self.widget.duration.setReadOnly(False)
            self.widget.iterations.setReadOnly(True)
        elif new_index == StopMode.Iterations:
            self.widget.duration.setEnabled(True)
            self.widget.iterations.setEnabled(True)
            self.widget.duration.setReadOnly(True)
            self.widget.iterations.setReadOnly(False)
        elif new_index == StopMode.IterationsTimeOut:
            self.widget.duration.setEnabled(True)
            self.widget.iterations.setEnabled(True)
            self.widget.duration.setReadOnly(False)
            self.widget.iterations.setReadOnly(False)
        self.recalculate()

    def on_loop_done(self, cancelled):
        self.widget.start_stop.setText('Start')
        self.widget.start_stop.setEnabled(True)
        self.widget.start_stop.setChecked(False)
        if self.widget.progress_bar.maximum():
            self.widget.progress_bar.setValue(
                self.widget.progress_bar.maximum())
        else:
            self.widget.progress_bar.setMaximum(1)
示例#9
0
class Loop(Backend):
    """The Loop backend allows you to execute task periodically.

    Usage:

        from lantz.ui.blocks import Loop, LoopUi

        def measure(counter, iterations, overrun):
            print(counter, iterations, overrun)
            data = osci.measure()
            print(data)

        app = Loop()

        app.body = measure

        start_gui_app(app, LoopUi)

    """

    #: Signal emitted before starting a new iteration
    #: Parameters: loop counter, iterations, overrun
    iteration = QtCore.Signal(int, int, bool)

    #: Signal emitted when the loop finished.
    #: The parameter is used to inform if the loop was canceled.
    loop_done = QtCore.Signal(bool)

    #: The function to be called. It requires three parameters.
    #:   counter - the iteration number
    #:   iterations - total number of iterations
    #:   overrun - a boolean indicating if the time required for the operation
    #:            is longer than the interval.
    #: :type: (int, int, bool) -> None
    body = None

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._active = False
        self._internal_func = None

    def stop(self):
        """Request the scanning to be stop.
        Will stop when the current iteration is finished.
        """
        self._active = False

    def start(self, body, interval=0, iterations=0, timeout=0):
        """Request the scanning to be started.

        :param body: function to be called at each iteration.
                     If None, the class body will be used.
        :param interval: interval between starts of the iteration.
                         If the body takes too long, the iteration will
                         be as fast as possible and the overrun flag will be True
        :param iterations: number of iterations
        :param timeout: total time in seconds that the scanning will take.
                        If overdue, the scanning will be stopped.
                        If 0, there is no timeout.
        """
        self._active = True
        body = body or self.body

        def internal(counter,
                     overrun=False,
                     schedule=QtCore.QTimer.singleShot):
            if not self._active:
                self.loop_done.emit(True)
                return

            st = time.time()
            self.iteration.emit(counter, iterations, overrun)
            body(counter, iterations, overrun)

            if iterations and counter + 1 == iterations:
                self._active = False
                self.loop_done.emit(False)
                return
            elif not self._active:
                self.loop_done.emit(True)
                return

            sleep = interval - (time.time() - st)
            schedule(sleep * 1000 if sleep > 0 else 0,
                     lambda: self._internal_func(counter + 1, sleep < 0))

        self._internal_func = internal
        if timeout:
            QtCore.QTimer.singleShot(timeout * 1000, self.stop)
        QtCore.QTimer.singleShot(0, lambda: self._internal_func(0))
示例#10
0
    def update_progress_bar(new, old):
        fraction = (new.magnitude - start.value()) / (stop.value() - start.value())
        progress.setValue(fraction * 100)

    inst.frequency_changed.connect(update_progress_bar)

    # <--------- New code--------->
    # Define a function to read the values from the widget and call scan_frequency
    class Scanner(QtCore.QObject):

        def scan(self):
            # Call the scan frequency
            scan_frequency(inst, start.value() * Hz, stop.value() * Hz,
                           step.value() * Hz, wait.value() * sec)
            # When it finishes, set the progress to 100%
            progress.setValue(100)

    thread = QtCore.QThread()
    scanner = Scanner()
    scanner.moveToThread(thread)
    thread.start()

    # Connect the clicked signal of the scan button to the function
    scan.clicked.connect(scanner.scan)

    qapp.aboutToQuit.connect(thread.quit)
    # <--------- End of new code --------->

    main.show()
    exit(qapp.exec_())
示例#11
0
class Scan(Backend):
    """A backend that iterates over an list of values,
    calling a `body` function in each step.
    """

    #: Signal emitted before starting a new iteration
    #: Parameters: loop counter, step value, overrun
    iteration = QtCore.Signal(int, int, bool)

    #: Signal emitted when the loop finished.
    #: The parameter is used to inform if the loop was canceled.
    loop_done = QtCore.Signal(bool)

    #: The function to be called. It requires three parameters.
    #:    counter - the iteration number.
    #:    current value - the current value of the scan.
    #:    overrun - a boolean indicating if the time required for the operation
    #:             is longer than the interval.
    #: :type: (int, int, bool) -> None
    body = None

    #: To be called before the body. Same signature as body
    _pre_body = None

    #: To be called after the body. Same signature as body
    _post_body = None

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._active = False
        self._internal_func = None

    def stop(self):
        """Request the scanning to be stop.
        Will stop when the current iteration is finished.
        """
        self._active = False

    def start(self, body, interval=0, steps=(), timeout=0):
        """Request the scanning to be started.

        :param body: function to be called at each iteration.
                     If None, the class body will be used.
        :param interval: interval between starts of the iteration.
                         If the body takes too long, the iteration will
                         be as fast as possible and the overrun flag will be True
        :param steps: iterable
        :param timeout: total time in seconds that the scanning will take.
                        If overdue, the scanning will be stopped.
                        If 0, there is no timeout.
        """
        self._active = True
        body = body or self.body

        iterations = len(steps)

        def internal(counter,
                     overrun=False,
                     schedule=QtCore.QTimer.singleShot):
            if not self._active:
                self.loop_done.emit(True)
                return

            st = time.time()
            self.iteration.emit(counter, iterations, overrun)
            if self._pre_body is not None:
                self._pre_body(counter, steps[counter], overrun)
            if body is not None:
                body(counter, steps[counter], overrun)
            if self._post_body is not None:
                self._post_body(counter, steps[counter], overrun)

            if iterations and counter + 1 == iterations:
                self._active = False
                self.loop_done.emit(False)
                return
            elif not self._active:
                self.loop_done.emit(True)
                return

            sleep = interval - (time.time() - st)
            schedule(sleep * 1000 if sleep > 0 else 0,
                     lambda: self._internal_func(counter + 1, sleep < 0))

        self._internal_func = internal
        if timeout:
            QtCore.QTimer.singleShot(timeout * 1000, self.stop)
        QtCore.QTimer.singleShot(0, lambda: self._internal_func(0))
示例#12
0
class ScanUi(Frontend):
    """A frontend to the Scan backend.

    Allows you to create linear sequence of steps between a start a stop,
    with selectable step size or number of steps.
    """

    gui = 'scan.ui'

    auto_connect = False

    #: Signal emitted when a start is requested.
    #: The parameters are None, interval, vector of steps
    request_start = QtCore.Signal(object, object, object)

    #: Signal emitted when a stop is requested.
    request_stop = QtCore.Signal()

    def connect_backend(self):
        super().connect_backend()

        self.widget.start_stop.clicked.connect(self.on_start_stop_clicked)
        self.widget.mode.currentIndexChanged.connect(self.on_mode_changed)

        self.widget.step_count.valueChanged.connect(self.recalculate)
        self.widget.start.valueChanged.connect(self.recalculate)
        self.widget.stop.valueChanged.connect(self.recalculate)
        self.widget.step_size.valueChanged.connect(self.recalculate)

        self.widget.progress_bar.setValue(0)

        self._ok_palette = QtGui.QPalette(self.widget.progress_bar.palette())
        self._overrun_palette = QtGui.QPalette(
            self.widget.progress_bar.palette())
        self._overrun_palette.setColor(QtGui.QPalette.Highlight,
                                       QtGui.QColor(QtCore.Qt.red))

        self.backend.iteration.connect(self.on_iteration)
        self.backend.loop_done.connect(self.on_loop_done)

        self.request_start.connect(self.backend.start)
        self.request_stop.connect(self.backend.stop)

    def on_start_stop_clicked(self, value=None):
        if self.backend._active:
            self.widget.start_stop.setText('...')
            self.widget.start_stop.setEnabled(False)
            self.request_stop.emit()
            return

        self.widget.start_stop.setText('Stop')
        self.widget.start_stop.setChecked(True)

        vals = [
            getattr(self.widget, name).value()
            for name in 'start stop step_size step_count wait'.split()
        ]
        start, stop, step_size, step_count, interval = vals

        steps = list(_linspace(start, stop, step_size))

        self.request_start.emit(None, interval, steps)

    def recalculate(self, *args):
        mode = self.widget.mode.currentIndex()
        if mode == StepsMode.step_size:
            step_size, length = _linspace_args(self.widget.start.value(),
                                               self.widget.stop.value(),
                                               self.widget.step_size.value())
            self.widget.step_count.setValue(length)
        elif mode == StepsMode.step_count:
            step_size, length = _linspace_args(
                self.widget.start.value(),
                self.widget.stop.value(),
                length=self.widget.step_count.value())
            self.widget.step_size.setValue(step_size)

    def on_iteration(self, counter, iterations, overrun):
        pbar = self.widget.progress_bar

        if not counter:
            if iterations:
                pbar.setMaximum(iterations + 1)
            else:
                pbar.setMaximum(0)

        if iterations:
            pbar.setValue(counter + 1)

        if overrun:
            pbar.setPalette(self._overrun_palette)
        else:
            pbar.setPalette(self._ok_palette)

    def on_mode_changed(self, new_index):
        if new_index == StepsMode.step_size:
            self.widget.step_count.setEnabled(False)
            self.widget.step_size.setEnabled(True)
        elif new_index == StepsMode.step_count:
            self.widget.step_count.setEnabled(True)
            self.widget.step_size.setEnabled(False)

        self.recalculate()

    def on_loop_done(self, cancelled):
        self.widget.start_stop.setText('Start')
        self.widget.start_stop.setEnabled(True)
        self.widget.start_stop.setChecked(False)
        if self.widget.progress_bar.maximum():
            self.widget.progress_bar.setValue(
                self.widget.progress_bar.maximum())
        else:
            self.widget.progress_bar.setMaximum(1)
示例#13
0
def initialize_and_report(widget,
                          drivers,
                          register_finalizer=True,
                          initializing_msg='Initializing ...',
                          initialized_msg='Initialized',
                          concurrent=True,
                          dependencies=None):
    """Initialize drivers while reporting the status in a QtWidget.

    :param widget: Qt Widget where the status information is going to be shown.
    :param drivers: iterable of drivers to initialize.
    :param register_finalizer: register driver.finalize method to be called at python exit.
    :param initializing_msg: message to be displayed while initializing.
    :param initialized_msg: message to be displayed after successful initialization.
    :param concurrent: indicates that drivers with satisfied dependencies
                       should be initialized concurrently.
    :param dependencies: indicates which drivers depend on others to be initialized.
                         each key is a driver name, and the corresponding
                         value is an iterable with its dependencies.
    :return: the QThread doing the initialization.
    """
    timing = {}

    thread = QtCore.QThread()
    helper = InitializerHelper(drivers, register_finalizer, concurrent,
                               dependencies)
    helper.moveToThread(thread)
    thread.helper = helper

    if isinstance(widget, QtGui.QTableWidget):

        def _initializing(driver):
            timing[driver] = time.time()
            row = drivers.index(driver)
            widget.setItem(row, 2, QtGui.QTableWidgetItem(initializing_msg))

        def _initialized(driver):
            delta = time.time() - timing[driver]
            row = drivers.index(driver)
            widget.setItem(
                row, 2,
                QtGui.QTableWidgetItem(initialized_msg +
                                       ' ({:.1f} sec)'.format(delta)))

        def _exception(driver, e):
            delta = time.time() - timing[driver]
            row = drivers.index(driver)
            widget.setItem(
                row, 2,
                QtGui.QTableWidgetItem('{} ({:.1f} sec)'.format(e, delta)))

        def _done(duration):
            widget.setItem(
                len(drivers), 2,
                QtGui.QTableWidgetItem('{:.1f} sec'.format(duration)))
            thread.quit()

        widget.clearContents()
        widget.setRowCount(len(drivers) + 1)
        for row, driver in enumerate(drivers):
            widget.setItem(row, 0, QtGui.QTableWidgetItem(driver.name))
            widget.setItem(row, 1,
                           QtGui.QTableWidgetItem(driver.__class__.__name__))
            widget.setItem(row, 2, QtGui.QTableWidgetItem(''))

        widget.resizeColumnToContents(0)
        widget.horizontalHeader().setStretchLastSection(True)

    elif isinstance(widget, QtGui.QLineEdit):

        def _initializing(driver):
            timing[driver] = time.time()
            widget.setText('{} ({}) > {}'.format(driver.name,
                                                 driver.__class__.__name__,
                                                 initializing_msg))

        def _initialized(driver):
            delta = time.time() - timing[driver]
            widget.setText('{} ({}) > {} ({:.1f} sec)'.format(
                driver.name, driver.__class__.__name__, initialized_msg,
                delta))

        def _exception(driver, e):
            delta = time.time() - timing[driver]
            widget.setText('{} ({}) > {} ({:.1f} sec)'.format(
                driver.name, driver.__class__.__name__, e, delta))

        def _done(duration):
            widget.setText('Initialized in {:.1f} sec'.format(duration))
            thread.quit()

        widget.setReadOnly(True)

    elif isinstance(widget, QtGui.QTextEdit):

        def _initializing(driver):
            timing[driver] = time.time()
            widget.append('{} ({}) > {}'.format(driver.name,
                                                driver.__class__.__name__,
                                                initializing_msg))

        def _initialized(driver):
            delta = time.time() - timing[driver]
            widget.append('{} ({}) > {} ({:.1f} sec)'.format(
                driver.name, driver.__class__.__name__, initialized_msg,
                delta))

        def _exception(driver, e):
            delta = time.time() - timing[driver]
            widget.append('{} ({}) > {} ({:.1f} sec)'.format(
                driver.name, driver.__class__.__name__, e, delta))

        def _done(duration):
            widget.append('Initialized in {:.1f} sec'.format(duration))
            thread.quit()

        widget.setReadOnly(True)

    else:
        raise TypeError('Unknown widget type {}.'.format(type(widget)))

    thread.started.connect(helper.process)
    helper.initializing.connect(_initializing)
    helper.initialized.connect(_initialized)
    helper.exception.connect(_exception)
    helper.finished.connect(_done)

    thread.start()
    return thread