Devices

Bases: type(ABC), type(QObject)

Metaclass enabling multiple inheritance of ABC and QObject.

Overview

Resolves metaclass conflicts so DeviceWorker can be both an ABC and a Qt QObject.

  • Combines behavior of ABC and QtCore.QObject metatypes.
Usage Notes

Use this metaclass only for worker classes that inherit from QObject.

Source code in contracts\device_worker.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class WorkerMeta(type(ABC), type(QtCore.QObject)):
    """
        Metaclass enabling multiple inheritance of ABC and QObject.

        Overview:
            Resolves metaclass conflicts so `DeviceWorker` can be both an ABC
            and a Qt QObject.

        - Combines behavior of `ABC` and `QtCore.QObject` metatypes.

        Usage Notes:
            Use this metaclass only for worker classes that inherit from QObject.
    """
    pass

Bases: ABC, QObject

Abstract base for device worker objects that run in a Qt context.

Overview

Defines the required API for workers that accept a DataSet, run a task, and accept data/processor types.

  • Required methods: set_data, run, set_data_type, set_processor_type.
  • Provides a short identifier used for run labelling.
Usage Notes

Concrete workers should implement thread-safe run logic and emit signals.

Source code in contracts\device_worker.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
class DeviceWorker(ABC, QtCore.QObject, metaclass=WorkerMeta):
    """
        Abstract base for device worker objects that run in a Qt context.

        Overview:
            Defines the required API for workers that accept a DataSet,
            run a task, and accept data/processor types.

        - Required methods: set_data, run, set_data_type, set_processor_type.
        - Provides a short `identifier` used for run labelling.

        Usage Notes:
            Concrete workers should implement thread-safe run logic and emit signals.
    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.identifier = str(uuid.uuid4())[:4]

    @abstractmethod
    def set_data(self, dataset: DataSet):
        pass

    @abstractmethod
    def run(self):
        pass

    @abstractmethod
    def set_data_type(self, data_type):
        pass

    @abstractmethod
    def set_processor_type(self, processor_type):
        pass

Bases: DeviceWorker

Concrete worker base implementing common setup and progress handling.

Overview

Implements option handling, dataset instantiation and a simple run flow that prepares data processors, emits progress, and calls a plot.

  • Manages device, dataset, plot_type, options, and data_processors.
  • Populates processors per-file and emits progress/finished signals.
  • set_data_type / set_processor_type are simple setters.
Usage Notes

Subclasses provide plotting methods referenced by plot_type and may extend run behaviour if needed.

Source code in contracts\device_worker.py
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
@decorate_class_with_logging(log_level=DEBUG_WORKER)
class DeviceWorkerCore(DeviceWorker):
    """
        Concrete worker base implementing common setup and progress handling.

        Overview:
            Implements option handling, dataset instantiation and a simple run
            flow that prepares data processors, emits progress, and calls a plot.

        - Manages `device`, `dataset`, `plot_type`, `options`, and `data_processors`.
        - Populates processors per-file and emits `progress`/`finished` signals.
        - `set_data_type` / `set_processor_type` are simple setters.

        Usage Notes:
            Subclasses provide plotting methods referenced by `plot_type` and may
            extend run behaviour if needed.
    """
    finished = QtCore.pyqtSignal()
    progress = QtCore.pyqtSignal(int)

    def __init__(self, device, dataset, plot_type, options: PlotterOptions):
        super().__init__()

        self.device = device
        self.dataset = dataset
        self.plot_type = plot_type

        self.options = options
        self.options.add_option(label="experiment_datetime", value = dataset.get_experiment_date())


        self.data_processors = None

        self.processor_type = None
        self.data_type = None

    def set_data_type(self, data_type):
        if not issubclass(data_type, Data):
            raise TypeError("data_type must be a subclass of Data")
        self.data_type = data_type

    def set_processor_type(self, processor_type):
        if not issubclass(processor_type, DataProcessor):
            raise TypeError("processor_type must be a subclass of DataProcessor")
        self.processor_type = processor_type

    def set_data(self, dataset: DataSet):
        if not isinstance(dataset, DataSet):
            raise TypeError("dataset must be an instance of DataSet")

        # CHECK: Check that dataset and processor types have been set
        # Initialise an empty dict and get the required filepaths
        self.data_processors = {}
        filepaths = dataset.get_filepaths()

        colours = dataset.get_all_colours()
        if colours is not None:
            self.options.add_option(label="colours", value=colours)

        # Progress housekeeping
        nr_of_files = len(filepaths)
        counter = 0

        # Read the dataset and instantiate a processor for each file
        for key in filepaths:
            data = self.data_type(key)
            data.read_file(filepaths[key])
            self.data_processors[key] = self.processor_type(data)

            # Emit progress signal
            counter += 1
            self.progress.emit(int(100*counter/nr_of_files))

    def run(self):
        # Set the data
        self.set_data(self.dataset)

        # Grab the correct plot and execute it, including uuid in the title
        title = f"{self.dataset.get_name()} (run {self.identifier})"
        plot_type = getattr(self, self.plot_type)
        plot_type(title=title)
        self.finished.emit()