Пример #1
0
    def test_steppeing_from_invalid_starting_point(self):

        the_value = -10

        def set_function(value):
            nonlocal the_value
            the_value = value

        def get_function():
            return the_value

        a = Parameter('test',
                      set_cmd=set_function,
                      get_cmd=get_function,
                      vals=Numbers(0, 100),
                      step=5)
        # We start out by setting the parameter to an
        # invalid value. This is not possible using initial_value
        # as the validator will catch that but perhaps this may happen
        # if the instrument can return out of range values.
        assert a.get() == -10
        with pytest.raises(ValueError):
            # trying to set to 10 should raise even with 10 valid
            # as the steps demand that we first step to -5 which is not
            a.set(10)
        # afterwards the value should still be the same
        assert a.get() == -10
Пример #2
0
def test_get_on_parameter_marked_as_non_gettable_raises():
    a = Parameter("param")
    a._gettable = False
    with pytest.raises(
            TypeError,
            match="Trying to get a parameter that is not gettable."):
        a.get()
Пример #3
0
def test_set_on_parameter_marked_as_non_settable_raises():
    a = Parameter("param", set_cmd=None)
    a.set(2)
    assert a.get() == 2
    a._settable = False
    with pytest.raises(
            TypeError,
            match="Trying to set a parameter that is not settable."):
        a.set(1)
    assert a.get() == 2
Пример #4
0
def test_scale_and_offset_raw_value_iterable_for_set_cache(values,
                                                           offsets,
                                                           scales):
    p = Parameter(name='test_scale_and_offset_raw_value', set_cmd=None)

    # test that scale and offset does not change the default behaviour
    p.cache.set(values)
    assert p.raw_value == values

    # test setting scale and offset does not change anything
    p.scale = scales
    p.offset = offsets
    assert p.raw_value == values

    np_values = np.array(values)
    np_offsets = np.array(offsets)
    np_scales = np.array(scales)
    np_get_latest_values = np.array(p.get_latest())
    # Without a call to ``get``, ``get_latest`` will just return old
    # cached values without applying the set scale and offset
    np.testing.assert_allclose(np_get_latest_values, np_values)
    np_get_values = np.array(p.get())
    # Now that ``get`` is called, the returned values are the result of
    # application of the scale and offset. Obviously, calling
    # ``get_latest`` now will also return the values with the applied
    # scale and offset
    np.testing.assert_allclose(np_get_values,
                               (np_values - np_offsets) / np_scales)
    np_get_latest_values_after_get = np.array(p.get_latest())
    np.testing.assert_allclose(np_get_latest_values_after_get,
                               (np_values - np_offsets) / np_scales)

    # test ``cache.set`` for scalar values
    if not isinstance(values, Iterable):
        p.cache.set(values)
        np.testing.assert_allclose(np.array(p.raw_value),
                                   np_values * np_scales + np_offsets)
        # No set/get cmd performed

        # testing conversion back and forth
        p.cache.set(values)
        np_get_latest_values = np.array(p.get_latest())
        # No set/get cmd performed
        np.testing.assert_allclose(np_get_latest_values, np_values)

    # adding statistics
    if isinstance(offsets, Iterable):
        event('Offset is array')
    if isinstance(scales, Iterable):
        event('Scale is array')
    if isinstance(values, Iterable):
        event('Value is array')
    if isinstance(scales, Iterable) and isinstance(offsets, Iterable):
        event('Scale is array and also offset')
    if isinstance(scales, Iterable) and not isinstance(offsets, Iterable):
        event('Scale is array but not offset')
Пример #5
0
    def test_gettable(self):
        p = Parameter('p', get_cmd=self.get_p)
        self._p = 21

        self.assertEqual(p(), 21)
        self.assertEqual(p.get(), 21)

        with self.assertRaises(NotImplementedError):
            p(10)

        self.assertTrue(hasattr(p, 'get'))
        self.assertFalse(hasattr(p, 'set'))
Пример #6
0
    def test_bare_function(self):
        # not a use case we want to promote, but it's there...
        p = Parameter('test', get_cmd=None, set_cmd=None)

        def doubler(x):
            p.set(x * 2)

        f = Function('f', call_cmd=doubler, args=[vals.Numbers(-10, 10)])

        f(4)
        self.assertEqual(p.get(), 8)
        with self.assertRaises(ValueError):
            f(20)
Пример #7
0
def test_get_cache_no_get():
    """
    Test that cache.get on a parameter that does not have get is handled
    correctly.
    """
    local_parameter = Parameter('test_param', set_cmd=None, get_cmd=False)
    # The parameter does not have a get method.
    with pytest.raises(AttributeError):
        local_parameter.get()
    # get_latest will fail as get cannot be called and no cache
    # is available
    with pytest.raises(RuntimeError):
        local_parameter.cache.get()
    value = 1
    local_parameter.set(value)
    assert local_parameter.cache.get() == value

    local_parameter2 = Parameter('test_param2', set_cmd=None,
                                 get_cmd=False, initial_value=value)
    with pytest.raises(AttributeError):
        local_parameter2.get()
    assert local_parameter2.cache.get() == value
Пример #8
0
def test_set_via_function():
    # not a use case we want to promote, but it's there...
    p = Parameter('test', get_cmd=None, set_cmd=None)

    def doubler(x):
        p.set(x * 2)

    f = Function('f', call_cmd=doubler, args=[vals.Numbers(-10, 10)])

    f(4)
    assert p.get() == 8
    with pytest.raises(ValueError):
        f(20)
Пример #9
0
def create_parameter(snapshot_get: bool,
                     snapshot_value: bool,
                     cache_is_valid: bool,
                     get_cmd: Optional[Union[Callable[..., Any], bool]],
                     offset: Union[str, float] = NOT_PASSED):
    kwargs: Dict[str, Any] = dict(set_cmd=None,
                                  label='Parameter',
                                  unit='a.u.',
                                  docstring='some docs')

    if offset != NOT_PASSED:
        kwargs.update(offset=offset)

    if snapshot_get != NOT_PASSED:
        kwargs.update(snapshot_get=snapshot_get)

    if snapshot_value != NOT_PASSED:
        kwargs.update(snapshot_value=snapshot_value)

    if get_cmd != NOT_PASSED:
        kwargs.update(get_cmd=get_cmd)

    p = Parameter('p', **kwargs)

    if get_cmd is not False:

        def wrap_in_call_counter(get_func):
            call_count = 0

            def wrapped_func(*args, **kwargs):
                nonlocal call_count
                call_count += 1
                return get_func(*args, **kwargs)

            wrapped_func.call_count = lambda: call_count

            return wrapped_func

        p.get = wrap_in_call_counter(p.get)
        # pre-condition
        assert p.get.call_count() == 0  # type: ignore[attr-defined]
    else:
        # pre-condition
        assert not hasattr(p, 'get')
        assert not p.gettable

    if cache_is_valid:
        p.set(42)

    return p
Пример #10
0
def array_in_str_dataset(experiment, request):
    meas = Measurement()
    scalar_param = Parameter('textparam', set_cmd=None)
    param = ArraySetPointParam()
    meas.register_parameter(scalar_param, paramtype='text')
    meas.register_parameter(param, setpoints=(scalar_param,),
                            paramtype=request.param)

    with meas.run() as datasaver:
        for i in ['A', 'B', 'C']:
            scalar_param.set(i)
            datasaver.add_result((scalar_param, scalar_param.get()),
                                 (param, param.get()))
    try:
        yield datasaver.dataset
    finally:
        datasaver.dataset.conn.close()
Пример #11
0
def array_in_scalar_dataset_unrolled(experiment):
    meas = Measurement()
    scalar_param = Parameter('scalarparam', set_cmd=None)
    param = ArraySetPointParam()
    meas.register_parameter(scalar_param)
    meas.register_parameter(param, setpoints=(scalar_param,),
                            paramtype='numeric')

    with meas.run() as datasaver:
        for i in range(1, 10):
            scalar_param.set(i)
            datasaver.add_result((scalar_param, scalar_param.get()),
                                 (param, param.get()))
    try:
        yield datasaver.dataset
    finally:
        datasaver.dataset.conn.close()
Пример #12
0
 def test_ramp_scaled(self, scale, value):
     p = Parameter('p',
                   set_cmd=self.set_p,
                   get_cmd=self.get_p,
                   scale=scale,
                   initial_value=0)
     assert p() == 0.0
     # first set a step size
     p.step = 0.1
     # and a wait time
     p.inter_delay = 1e-9  # in seconds
     expected_output = np.linspace(0.1, 10, 100)
     np.testing.assert_allclose(p.get_ramp_values(10, p.step),
                                expected_output)
     p.set(value)
     np.testing.assert_allclose(p.get(), value)
     assert p.raw_value == value * scale
Пример #13
0
def varlen_array_in_scalar_dataset(experiment):
    meas = Measurement()
    scalar_param = Parameter('scalarparam', set_cmd=None)
    param = ArraySetPointParam()
    meas.register_parameter(scalar_param)
    meas.register_parameter(param, setpoints=(scalar_param,),
                            paramtype='array')
    np.random.seed(0)
    with meas.run() as datasaver:
        for i in range(1, 10):
            scalar_param.set(i)
            param.setpoints = (np.arange(i),)
            datasaver.add_result((scalar_param, scalar_param.get()),
                                 (param, np.random.rand(i)))
    try:
        yield datasaver.dataset
    finally:
        datasaver.dataset.conn.close()
Пример #14
0
def test_gettable():
    mem = ParameterMemory()
    p = Parameter('p', get_cmd=mem.get)
    mem.set(21)

    assert p() == 21
    assert p.get() == 21

    with pytest.raises(NotImplementedError):
        p(10)

    assert hasattr(p, 'get')
    assert p.gettable
    assert not hasattr(p, 'set')
    assert not p.settable

    p.cache.set(7)
    assert p.get_latest() == 7
    # Nothing has been passed to the "instrument" at ``cache.set``
    # call, hence the following assertions should hold
    assert mem.get() == 21
    assert p() == 21
    assert p.get_latest() == 21
Пример #15
0
def array_in_scalar_dataset_unrolled(experiment):
    """
    This fixture yields a dataset where an array-valued parameter is registered
    as a 'numeric' type and has an additional single-valued setpoint. We
    expect data to be saved as individual scalars, with the scalar setpoint
    repeated.
    """
    meas = Measurement()
    scalar_param = Parameter('scalarparam', set_cmd=None)
    param = ArraySetPointParam()
    meas.register_parameter(scalar_param)
    meas.register_parameter(param,
                            setpoints=(scalar_param, ),
                            paramtype='numeric')

    with meas.run() as datasaver:
        for i in range(1, 10):
            scalar_param.set(i)
            datasaver.add_result((scalar_param, scalar_param.get()),
                                 (param, param.get()))
    try:
        yield datasaver.dataset
    finally:
        datasaver.dataset.conn.close()
Пример #16
0
class VideoMode:
    """ Controls the videomode tuning.

    Attributes:
        station (qcodes station): contains all the information about the set-up
        sweepparams (string, 1 x 2 list or dict): the parameter(s) to be swept
        sweepranges (int or 1 x 2 list): the range(s) to be swept over (peak-to-peak range)
        minstrument (int or tuple): the channel of the FPGA, or tuple (instrument, channel)
        Naverage (int): the number of times the FPGA averages
        resolution (1 x 2 list): for 2D the resolution
        nplots (int or None): number of plots to show. must be equal to the number of channels in the minstrument argument
        sample_rate (float): sample rate for acquisition device
        crosshair (bool): enable crosshair
    """

    videomode_class_index = 0

    def __init__(self, station, sweepparams, sweepranges, minstrument, nplots=None, Naverage=10,
                 resolution=[96, 96], sample_rate='default', diff_dir=None, verbose=1,
                 dorun=True, show_controls=True, add_ppt=True, crosshair=False, averaging=True, name=None,
                 mouse_click_callback=None):
        """ Tool for fast acquisition of charge stability diagram

        The class assumes that the station has a station.digitizer and a station.awg.

        Args:
            station (QCoDeS station): reference for the instruments
            sweepparams (list or dict or str): parameters to sweep
            mouse_click_callback (None or function): if None then update scan position with callback

        """
        if VideoMode.videomode_class_index == 0:
            # create instance of QApplication
            _ = pyqtgraph.mkQApp()
        VideoMode.videomode_class_index = VideoMode.videomode_class_index + 1  # increment index of VideoMode objects
        self.videomode_index = VideoMode.videomode_class_index

        self.station = station
        self.verbose = verbose
        self.sweepparams = sweepparams
        self.sweepranges = sweepranges
        self.virtual_awg = getattr(station, ' virtual_awg', None)

        self.minstrumenthandle = minstrument[0]
        self.channels = minstrument[1]
        if isinstance(self.channels, int):
            self.channels = [self.channels]

        self.Naverage = Parameter('Naverage', get_cmd=self._get_Naverage,
                                  set_cmd=self._set_Naverage, vals=Numbers(1, 1023))
        self._Naverage_val = Naverage
        self.resolution = resolution
        self.sample_rate = sample_rate
        self.diff_dir = diff_dir
        self.datalock = threading.Lock()
        self.datafunction_result = None
        self.update_sleep = 1e-5

        if name is None:
            name = 'VideoMode %d' % self.videomode_index
            if isinstance(self.sweepparams, (str, list)):
                name += ': %s' % str(self.sweepparams)
        self.name = name

        # parse instrument
        minstrumenthandle = qtt.measurements.scans.get_instrument(self.minstrumenthandle, station)

        if minstrumenthandle.name in ['digitizer', 'm4i']:
            if sample_rate == 'default':
                self.sampling_frequency = minstrumenthandle.sample_rate
            else:
                minstrumenthandle.sample_rate(sample_rate)
                self.sampling_frequency = station.digitizer.sample_rate
        elif minstrumenthandle.name in ['ZIUHFLI', 'ziuhfli']:
            self.sampling_frequency = minstrumenthandle.scope_samplingrate
        else:
            try:
                minstrumenthandle = qtt.measurements.scans.get_instrument(
                    minstrument, station)
                self.sampling_frequency = minstrumenthandle.sample_rate
            except:
                raise Exception('no fpga or digitizer found')

        # for addPPT and debugging
        self.scanparams = {'sweepparams': sweepparams, 'sweepranges': sweepranges, 'minstrument': minstrument,
                           'resolution': self.resolution, 'sampling_frequency': self.sampling_frequency()}
        self.idx = 0
        self.fps = qtt.pgeometry.fps_t(nn=6)

        # Setup the GUI
        if nplots is None:
            nplots = len(self.channels)
        self.nplots = nplots
        self.window_title = "%s: nplots %d" % (
            self.name, self.nplots)

        win = QtWidgets.QWidget()
        win.setWindowTitle(self.window_title)
        self.mainwin = win

        vertLayout = QtWidgets.QVBoxLayout()
        self.topLayout = None
        if show_controls:
            topLayout = QtWidgets.QHBoxLayout()
            win.start_button = QtWidgets.QPushButton('Start')
            win.stop_button = QtWidgets.QPushButton('Stop')
            topLayout.addWidget(win.start_button)
            topLayout.addWidget(win.stop_button)
            if add_ppt:
                win.ppt_button = QtWidgets.QPushButton('Copy to PPT')
                win.ppt_button.clicked.connect(connect_slot(self.addPPT))
                topLayout.addWidget(win.ppt_button)

            win.averaging_box = QtWidgets.QCheckBox('Averaging')

            for b in [win.start_button, win.stop_button]:
                b.setMaximumHeight(24)

            topLayout.addWidget(win.averaging_box)
            vertLayout.addLayout(topLayout)
            self.topLayout = topLayout

        win.setLayout(vertLayout)

        self.plotLayout = QtWidgets.QHBoxLayout()
        vertLayout.addLayout(self.plotLayout)

        self.lp = []
        for ii in range(nplots):
            lp = livePlot(None, self.station.gates,
                          self.sweepparams, self.sweepranges, show_controls=False,
                          plot_title=str(self.minstrumenthandle) + ' ' + str(self.channels[ii]))
            self.lp.append(lp)
            self.plotLayout.addWidget(self.lp[ii].win)

        if show_controls:
            self.mainwin.averaging_box.clicked.connect(
                connect_slot(self.enable_averaging_slot))

        self.mainwin.start_button.clicked.connect(connect_slot(self.run))
        self.mainwin.stop_button.clicked.connect(connect_slot(self.stop))
        self.box = self._create_naverage_box(Naverage=Naverage)
        self.topLayout.addWidget(self.box)

        self.setGeometry = self.mainwin.setGeometry
        self.mainwin.resize(800, 600)
        self.mainwin.show()

        self.timer = qtpy.QtCore.QTimer()
        self.timer.timeout.connect(self.updatebg)

        self.crosshair(show=crosshair)

        self.enable_averaging_slot(averaging=averaging)

        if mouse_click_callback is None:
            mouse_click_callback = self._update_position
        for p in self.lp:
            p.sigMouseClicked.connect(mouse_click_callback)

        if dorun:
            self.run()

    def __repr__(self):
        s = '%s: %s at 0x%x' % (self.__class__.__name__, self.name, id(self))
        return s

    def _update_position(self, position, verbose=1):
        """ Update position of gates with selected point

        Args:
            position (array): point with new coordinates
            verbose (int): verbosity level
        """
        station = self.station

        if verbose:
            print('# %s: update position: %s' % (self.__class__.__name__, position,))

        if self.scan_dimension() == 1:
            delta = position[0]
            if isinstance(self.scanparams['sweepparams'], str):
                param = getattr(station.gates, self.scanparams['sweepparams'])
                param.set(delta)
                if verbose > 2:
                    print('  set %s to %s' % (param, delta))
                return
        try:
            for ii, parameter in enumerate(self.scanparams['sweepparams']):
                delta = position[ii]
                if verbose:
                    print('param %s: delta %.3f' % (parameter, delta))

                if isinstance(parameter, str):
                    if parameter == 'gates_horz' or parameter == 'gates_vert':
                        d = self.scanparams['sweepparams'][parameter]
                        for g, f in d.items():
                            if verbose >= 2:
                                print('  %s: increment %s with %s' % (parameter, g, f * delta))
                            param = getattr(station.gates, g)
                            param.increment(f * delta)
                    else:
                        if verbose > 2:
                            print('  set %s to %s' % (parameter, delta))
                        param = getattr(station.gates, parameter)
                        param.set(delta)
                else:
                    raise Exception('_update_position with parameter type %s not supported' % (type(parameter),))
        except Exception as ex:
            logging.exception(ex)

    def enable_averaging_slot(self, averaging=None, *args, **kwargs):
        """ Update the averaging mode of the widget """
        if averaging is None:
            self._averaging_enabled = self.mainwin.averaging_box.checkState()
        else:
            self._averaging_enabled = averaging
            self.mainwin.averaging_box.setChecked(self._averaging_enabled)

        for l in self.lp:
            l.enable_averaging(self._averaging_enabled)

    def addPPT(self):
        """ Copy image of videomode window to PPT """
        isrunning = self.is_running()
        if isrunning:
            self.stopreadout()  # prevent multi-threading issues
            time.sleep(0.2)

        setting_notes = 'moving averaging enabled: %d\n' % self._averaging_enabled
        setting_notes += 'number of averages %d\n' % self.Naverage()

        qtt.utilities.tools.addPPTslide(fig=self, title='VideoMode %s' % self.name,
                                        notes=self.station,
                                        extranotes='date: %s' % (qtt.data.dateString(),) + '\n' + 'scanjob: ' + str(
                                            self.scanparams) + '\n\n' + setting_notes)
        if isrunning:
            self.startreadout()

    def updatebg(self):
        """ Update function for the widget 

        Calls the datafunction() and update() function for all subplots
        """
        if self.idx % 10 == 0:
            logging.debug('%s: updatebg %d' %
                          (self.__class__.__name__, self.idx))
        self.idx = self.idx + 1
        self.fps.addtime(time.time())
        if self.datafunction is not None:
            try:
                dd = self.datafunction()
                self.datafunction_result = dd
                for ii, d in enumerate(dd):
                    self.lp[ii].update(data=d, processevents=False)
                pyqtgraph.mkQApp().processEvents()
            except Exception as e:
                logging.exception(e)
                print('%s: Exception in updatebg, stopping readout' %
                      self.__class__.__name__)
                self.stopreadout()
        else:
            self.stopreadout()
            dd = None

        self.mainwin.setWindowTitle(
            self.window_title + ' %.1f [fps]' % self.fps.framerate())

        if self.fps.framerate() < 10:
            time.sleep(0.1)
        time.sleep(self.update_sleep)

    def is_running(self):
        return self.timer.isActive()

    def startreadout(self, callback=None, rate=30, maxidx=None):
        """
        Args:
            rate (float): sample rate in ms

        """
        if maxidx is not None:
            self.maxidx = maxidx
        if callback is not None:
            self.datafunction = callback
        self.timer.start(1000 * (1. / rate))
        if self.verbose:
            print('%s: start readout' % (self.__class__.__name__,))

    def stopreadout(self):
        if self.verbose:
            print('%s: stop readout' % (self.__class__.__name__,))
        self.timer.stop()
        self.mainwin.setWindowTitle(self.window_title + ' stopped')

    def _create_naverage_box(self, Naverage=1):
        box = QtWidgets.QSpinBox()
        box.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)
        # do not emit signals when still editing
        box.setKeyboardTracking(False)
        box.setMinimum(1)
        box.setMaximum(1023)
        box.setPrefix('Naverage: ')
        box.setValue(Naverage)
        box.setMaximumWidth(120)
        box.valueChanged.connect(self.Naverage.set)
        return box

    def close(self):
        self.mainwin.close()

    def get_dataset(self, run=False):
        """ Return latest recorded dataset

        Returns:
            alldata (dataset or list of datasets)

        """
        with self.datalock:
            if run:
                warnings.warn('not supported')
                # [l.datafunction_result for l in self.lp]
                data = self.datafunction_result
                data = np.array(data)
            else:
                data = self.datafunction_result
                if data is not None:
                    data = np.array(data)
            if data is not None:
                self.alldata = self.makeDataset(data, Naverage=None)
            return self.alldata

    def scan_dimension(self):
        if isinstance(self.sweepranges, (float, int)):
            return 1
        elif isinstance(self.sweepranges, list):
            if len(self.sweepranges) == 2:
                return 2
            elif len(self.sweepranges) == 1:
                return 1
            else:
                raise Exception('scan dimension not supported')
        else:
            return -1

    def run(self, start_readout=True):
        """ Programs the AWG, starts the read-out and the plotting. """
        if self.verbose:
            print('%s: run ' % (self.__class__.__name__,) )
        scan_dimensions = self.scan_dimension()
        virtual_awg = getattr(self.station, 'virtual_awg', None)
        awg = getattr(self.station, 'awg', None)

        if scan_dimensions == 1:
            self.__run_1d_scan(awg, virtual_awg)
        elif scan_dimensions == 2:
            self.__run_2d_scan(awg, virtual_awg)
        else:
            raise Exception('type of scan not supported')

        if start_readout:
            if self.verbose:
                print('%s: run: startreadout' % (self.__class__.__name__,))
            self.startreadout()

    def __run_1d_scan(self, awg, virtual_awg, period=1e-3):
        if virtual_awg:
            if not isinstance(self.sweepparams, (str, dict)):
                raise Exception('arguments not supported')
            sweep_range = self.sweepranges
            gates = self.sweepparams if isinstance(self.sweepparams, dict) else {self.sweepparams: 1}
            waveform = virtual_awg.sweep_gates(gates, sweep_range, period)
            virtual_awg.enable_outputs(list(gates.keys()))
            virtual_awg.run()
        else:
            if type(self.sweepparams) is str:
                waveform, _ = awg.sweep_gate(self.sweepparams, self.sweepranges, period=period)
            elif type(self.sweepparams) is dict:
                waveform, _ = awg.sweep_gate_virt(self.sweepparams, self.sweepranges, period=period)
            else:
                raise Exception('arguments not supported')
        self.datafunction = videomode_callback(self.station, waveform, self.Naverage.get(),
                                               minstrument=(self.minstrumenthandle, self.channels))

    def __run_2d_scan(self, awg, virtual_awg, period=None):
        if virtual_awg:
            sweep_ranges = [i for i in self.sweepranges]
            if isinstance(self.sweepparams[0], dict):
                gates = self.sweepparams
            else:
                # convert list of strings to dict format
                gates = self.sweepparams
                gates = [dict([(gate, 1)]) for gate in gates]

            if period is None:
                sampling_frequency = qtt.measurements.scans.get_sampling_frequency(self.minstrumenthandle)
                base_period = 1. / sampling_frequency
                total_period = base_period * np.prod(self.resolution)
            else:
                total_period = period
                warnings.warn('code untested')
            waveform = virtual_awg.sweep_gates_2d(gates, sweep_ranges, total_period, self.resolution)
            keys = [list(item.keys())[0] for item in gates]
            virtual_awg.enable_outputs(keys)
            virtual_awg.run()
        else:
            if isinstance(self.sweepparams, list):
                waveform, _ = awg.sweep_2D(self.sampling_frequency.get(), self.sweepparams,
                                           self.sweepranges, self.resolution)
            elif isinstance(self.sweepparams, dict):
                waveform, _ = awg.sweep_2D_virt(self.sampling_frequency.get(), self.sweepparams[
                    'gates_horz'], self.sweepparams['gates_vert'], self.sweepranges, self.resolution)
            else:
                raise Exception('arguments not supported')
            if self.verbose:
                print('%s: 2d scan, define callback ' % (self.__class__.__name__,))
        self.datafunction = videomode_callback(self.station, waveform, self.Naverage.get(),
                                               minstrument=(self.minstrumenthandle, self.channels),
                                               resolution=self.resolution, diff_dir=self.diff_dir)

    def stop(self):
        """ Stops the plotting, AWG(s) and if available RF. """
        self.stopreadout()
        if self.station is not None:
            if hasattr(self.station, 'awg'):
                self.station.awg.stop()
            if hasattr(self.station, 'RF'):
                self.station.RF.off()
            if hasattr(self.station, 'virtual_awg'):
                self.station.virtual_awg.stop()

    def single(self):
        """Do a single scan with a lot averaging.

        Note: this does not yet support the usage of linear combinations of gates (a.k.a. virtual gates).
        """
        raise Exception('not implemented')

    def crosshair(self, *args, **kwargs):
        for l in self.lp:
            l.crosshair(*args, **kwargs)

    def makeDataset(self, data, Naverage=None):
        """ Helper function """
        metadata = {'scantime': str(datetime.datetime.now(
        )), 'station': self.station.snapshot(), 'allgatevalues': self.station.gates.allvalues()}
        metadata['Naverage'] = Naverage
        if hasattr(self.datafunction, 'diff_dir'):
            metadata['diff_dir'] = self.datafunction.diff_dir

        if data.ndim == 2:
            if (data.shape[0] > 1):
                raise Exception('not yet implemented')
            data = data[0]
            alldata, _ = makeDataset_sweep(data, self.sweepparams, self.sweepranges,
                                           gates=self.station.gates, loc_record={'label': 'videomode_1d_single'})
            alldata.metadata = metadata
        elif data.ndim == 3:
            if (data.shape[0] > 1):
                warnings.warn('getting dataset for multiple dimensions not yet tested')

            import copy
            alldata = [None] * len(data)
            for jj in range(len(data)):
                datax = data[jj]
                alldatax, _ = makeDataset_sweep_2D(datax, self.station.gates, self.sweepparams, self.sweepranges,
                                                   loc_record={
                                                       'label': 'videomode_2d_single'})
                alldatax.metadata = copy.copy(metadata)
                alldata[jj] = alldatax
        else:
            raise Exception('makeDataset: data.ndim %d' % data.ndim)
        return alldata

    def _get_Naverage(self):
        return self._Naverage_val

    def _set_Naverage(self, value):
        self._Naverage_val = value
        self.datafunction.Naverage = value
        self.box.setValue(value)

    @staticmethod
    def all_instances(verbose=1):
        """ Return all VideoMode instances """
        lst = qtt.pgeometry.list_objects(VideoMode, verbose=verbose)
        return lst

    @staticmethod
    def get_instance(idx):
        """ Return instance by index """
        lst = VideoMode.all_instances(verbose=0)
        for l in lst:
            if l.videomode_index == idx:
                return l
        return None

    @staticmethod
    def stop_all_instances():
        """ Stop readout on all all VideoMode instances """
        lst = qtt.pgeometry.list_objects(VideoMode)
        for v in lst:
            v.stopreadout()