Exemple #1
0
 def test_filtered_list_changing_container_under_changes_retains_order(
         self):
     # the bug was that if list model is under a change and items are only
     # being rearranged (easy to occur with dependent list being sorted differently)
     # then there is no way to detect that it is not sorted anymore so it proceeds
     # as if it is already sorted properly.
     l = ListModel.ListModel("items")
     l.append_item("3")
     l.append_item("1")
     l.append_item("4")
     l.append_item("2")
     l2 = ListModel.ListModel("items")
     l2.append_item("4")
     l2.append_item("1")
     l2.append_item("2")
     l2.append_item("3")
     l3 = ListModel.FilteredListModel(container=l, items_key="items")
     self.assertEqual(["3", "1", "4", "2"], l3.items)
     l4 = ListModel.FilteredListModel(container=l3, items_key="items")
     self.assertEqual(["3", "1", "4", "2"], l4.items)
     l4.begin_change()
     l3.begin_change()
     l3.filter = ListModel.Filter(True)
     l3.end_change()
     l4.end_change()
     l4.begin_change()
     l3.begin_change()
     l3.container = l2
     l3.end_change()
     l4.end_change()
     self.assertEqual(["4", "1", "2", "3"], l3.items)
     self.assertEqual(["4", "1", "2", "3"], l4.items)
Exemple #2
0
 def test_flattened_model_with_empty_master_item_closes_properly(self):
     l = ListModel.ListModel("as")
     bs1 = ListModel.ListModel("bs")
     bs1.append_item("11")
     bs2 = ListModel.ListModel("bs")
     bs3 = ListModel.ListModel("bs")
     bs3.append_item("31")
     l.append_item(bs1)
     l.append_item(bs2)
     l.append_item(bs3)
     f = ListModel.FlattenedListModel(container=l, master_items_key="as", child_items_key="bs", items_key="cs")
     with contextlib.closing(f):
         pass
Exemple #3
0
 def test_flattened_model_initializes_properly(self):
     l = ListModel.ListModel("as")
     bs1 = ListModel.ListModel("bs")
     bs1.append_item("11")
     bs1.append_item("12")
     bs2 = ListModel.ListModel("bs")
     bs3 = ListModel.ListModel("bs")
     bs3.append_item("31")
     bs3.append_item("32")
     bs3.append_item("33")
     l.append_item(bs1)
     l.append_item(bs2)
     l.append_item(bs3)
     f = ListModel.FlattenedListModel(container=l, master_items_key="as", child_items_key="bs", items_key="cs")
     self.assertEqual(["11", "12", "31", "32", "33"], f.cs)
Exemple #4
0
 def slow_test_threaded_filtered_model_updates(self):
     for _ in range(1000):
         list_model = ListModel.ListModel("data_items")
         filtered_data_items = ListModel.FilteredListModel(items_key="data_items")
         data_items = list()
         cc = 30
         for _ in range(cc):
             data_item = DataItem.DataItem(numpy.zeros((16, 16), numpy.uint32))
             data_items.append(data_item)
         c1 = [n for n in range(cc) if random.randint(0,100) > 50]
         def is_live_filter(data_item):
             return data_items.index(data_item) in c1
         filtered_data_items.container = list_model
         filtered_data_items.sort_key = lambda x: data_items.index(x)
         selection = Selection.IndexedSelection()
         filtered_data_items2 = ListModel.FilteredListModel(items_key="data_items", container=filtered_data_items, selection=selection)
         filtered_data_items2.filter = ListModel.PredicateFilter(is_live_filter)
         finished = threading.Event()
         def update_randomly():
             for _ in range(cc):
                 data_item = random.choice(filtered_data_items._get_master_items())
                 data_item.data_item_changed_event.fire()
             finished.set()
         list_model.insert_item(0, data_items[0])
         threading.Thread(target = update_randomly).start()
         for index in range(1, cc):
             list_model.insert_item(index, data_items[index])
         finished.wait()
         filtered_data_items2.close()
         filtered_data_items.close()
Exemple #5
0
    def __init__(self):
        self.__config_file = None

        # the active video sources (hardware sources). this list is updated when a video camera device is registered or
        # unregistered with the hardware source manager.
        self.video_sources = ListModel.ListModel()

        # the list of instances of video cameras. this is similar to the video sources but is the devices plus settings
        # for the device. some devices might not have instances if the factory to create the instance hasn't been
        # registered yet.
        self.__instances = list()

        # the list of video device factories. this is populated by responding to the registry messages.
        self.__video_device_factories = list()

        def component_registered(component, component_types):
            if "video_device_factory" in component_types:
                if not component in self.__video_device_factories:
                    self.__video_device_factories.append(component)
                self.__make_video_devices()

        def component_unregistered(component, component_types):
            if "video_device_factory" in component_types:
                if component in self.__video_device_factories:
                    self.__video_device_factories.remove(component)

        self.__component_registered_listener = Registry.listen_component_registered_event(
            component_registered)
        self.__component_unregistered_listener = Registry.listen_component_unregistered_event(
            component_unregistered)

        for component in Registry.get_components_by_type(
                "video_device_factory"):
            component_registered(component, {"video_device_factory"})
Exemple #6
0
    def test_filtered_list_sends_begin_end_changes_for_grouped_insert_and_remove(
            self):
        l = ListModel.ListModel("items")
        l.append_item("3")
        l.append_item("1")
        l.append_item("4")
        l.append_item("2")
        l2 = ListModel.FilteredListModel(container=l, items_key="items")
        l2.sort_key = lambda x: x

        begin_changes_count = 0
        end_changes_count = 0

        def begin_changes(key):
            nonlocal begin_changes_count
            begin_changes_count += 1

        def end_changes(key):
            nonlocal end_changes_count
            end_changes_count += 1

        with l2.begin_changes_event.listen(
                begin_changes), l2.end_changes_event.listen(end_changes):
            with l2.changes():
                l.insert_item(0, "5")
                l.insert_item(0, "6")
                l.remove_item(0)
                l.remove_item(0)

        self.assertEqual(1, begin_changes_count)
        self.assertEqual(1, end_changes_count)
Exemple #7
0
    def test_mapped_list_sends_begin_end_changes_for_single_insert_and_remove(
            self):
        l = ListModel.ListModel("items")
        l.append_item("3")
        l.append_item("1")
        l.append_item("4")
        l.append_item("2")
        l1 = ListModel.FilteredListModel(container=l,
                                         master_items_key="items",
                                         items_key="mitems")
        l1.sort_key = lambda x: x
        l2 = ListModel.MappedListModel(container=l1,
                                       master_items_key="mitems",
                                       items_key="items")

        begin_changes_count = 0
        end_changes_count = 0

        def begin_changes(key):
            nonlocal begin_changes_count
            begin_changes_count += 1

        def end_changes(key):
            nonlocal end_changes_count
            end_changes_count += 1

        with l2.begin_changes_event.listen(
                begin_changes), l2.end_changes_event.listen(end_changes):
            l.insert_item(0, "5")
            l.remove_item(0)

        self.assertEqual(2, begin_changes_count)
        self.assertEqual(2, end_changes_count)
Exemple #8
0
 def test_random_filtered_model_updates(self):
     list_model = ListModel.ListModel("data_items")
     filtered_data_items = ListModel.FilteredListModel(items_key="data_items")
     filtered_data_items.container = list_model
     data_items = list()
     cc = 30
     for _ in range(cc):
         data_item = DataItem.DataItem(numpy.zeros((16, 16), numpy.uint32))
         list_model.insert_item(0, data_item)
         data_items.append(data_item)
     selection = Selection.IndexedSelection()
     filtered_data_items2 = ListModel.FilteredListModel(items_key="data_items", container=filtered_data_items, selection=selection)
     import random
     for xx in range(10):
         c1 = [n for n in range(cc)]
         c2 = [n for n in range(cc) if random.randint(0,100) > 20]
         random.shuffle(c1)
         random.shuffle(c2)
         def is_live_filter(data_item):
             return data_items.index(data_item) in c1
         def is_live_filter2(data_item):
             return data_items.index(data_item) in c2
         filtered_data_items.sort_key = lambda x: data_items.index(x)
         with filtered_data_items.changes():
             filtered_data_items.filter = ListModel.PredicateFilter(is_live_filter)
             filtered_data_items.filter = ListModel.PredicateFilter(is_live_filter2)
         self.assertEqual(set(c2), set([data_items.index(d) for d in filtered_data_items2.items]))
     for data_item in data_items:
         data_item.close()
Exemple #9
0
 def test_initial_mapped_model_values_are_correct(self):
     l = ListModel.ListModel("items")
     l.append_item(A("1"))
     l.append_item(A("2"))
     l2 = ListModel.MappedListModel(container=l, master_items_key="items", items_key="itemsb", map_fn=B)
     self.assertEqual([b.s for b in map(B, l.items)], [b.s for b in l2.items])
     self.assertEqual(l2.itemsb, l2.items)
     self.assertEqual("2_B", l2.items[1].s)
Exemple #10
0
 def create_objects(self,
                    name: str,
                    items,
                    label: str = None) -> ComputationVariable:
     list_model = ListModel.ListModel(items=items)
     variable = ComputationVariable(name, objects=list_model, label=label)
     self.add_variable(variable)
     return variable
Exemple #11
0
 def read_from_dict(self, properties: dict) -> None:
     # used for persistence
     # ensure that value_type is read first
     value_type_property = self._get_persistent_property("value_type")
     value_type_property.read_from_dict(properties)
     super().read_from_dict(properties)
     if self.object_specifiers:
         self.__objects_model = ListModel.ListModel(
             items=self.object_specifiers)
Exemple #12
0
 def test_mapped_model_selection_after_insert_are_correct(self):
     l = ListModel.ListModel("items")
     l.append_item(A("1"))
     l.append_item(A("2"))
     l2 = ListModel.MappedListModel(container=l, master_items_key="items", items_key="itemsb", map_fn=B)
     s = l2.make_selection()
     s.add(0)
     s.add(1)
     l.insert_item(1, A("1.5"))
     self.assertEqual({0, 2}, s.indexes)
Exemple #13
0
 def test_should_be_able_to_get_items_from_list_binding(self):
     list_model = ListModel.ListModel("items")
     list_model.insert_item(0, "zero")
     list_model.insert_item(1, "one")
     list_model.insert_item(2, "two")
     binding = Binding.ListBinding(list_model, "items")
     items = binding.items
     self.assertEqual(len(items), 3)
     self.assertEqual(items[2], "two")
     list_model.insert_item(0, "negative")
     self.assertEqual(len(items), 4)
     self.assertEqual(items[3], "two")
Exemple #14
0
 def test_inserting_items_into_model_index0_without_sort_key_puts_them_in_same_order(self):
     list_model = ListModel.ListModel("data_items")
     filtered_data_items = ListModel.FilteredListModel(items_key="data_items")
     filtered_data_items.container = list_model
     data_items = list()
     for index, value in enumerate(TestDataItemsModelModule.values):
         data_item = DataItem.DataItem(numpy.zeros((16, 16), numpy.uint32))
         data_item.title = value
         list_model.insert_item(TestDataItemsModelModule.indexes[index], data_item)
         data_items.append(data_item)
     self.assertEqual([d.title for d in filtered_data_items.items], TestDataItemsModelModule.result)
     for data_item in data_items:
         data_item.close()
Exemple #15
0
 def test_inserting_items_into_model_index0_with_sort_key_puts_them_in_correct_order(self):
     list_model = ListModel.ListModel("data_items")
     filtered_data_items = ListModel.FilteredListModel(items_key="data_items")
     filtered_data_items.container = list_model
     filtered_data_items.sort_key = operator.attrgetter("title")
     data_items = list()
     for value in TestDataItemsModelModule.values:
         data_item = DataItem.DataItem(numpy.zeros((16, 16), numpy.uint32))
         data_item.title = value
         list_model.insert_item(0, data_item)
         data_items.append(data_item)
     self.assertEqual([d.title for d in filtered_data_items.items], sorted([d.title for d in filtered_data_items.items]))
     for data_item in data_items:
         data_item.close()
Exemple #16
0
 def test_filtered_list_changing_from_sorted_to_unsorted_retains_order(self):
     l = ListModel.ListModel("items")
     l.append_item("3")
     l.append_item("1")
     l.append_item("4")
     l.append_item("2")
     l2 = ListModel.FilteredListModel(container=l, items_key="items")
     l2.sort_key = lambda x: x
     self.assertEqual(["1", "2", "3", "4"], l2.items)
     l2.filter = ListModel.PredicateFilter(lambda x: x != "4")
     l2.sort_key = None
     self.assertEqual(["3", "1", "2"], l2.items)
     l.remove_item(1)
     self.assertEqual(["3", "2"], l2.items)
Exemple #17
0
 def test_filtered_list_is_sorted(self):
     l = ListModel.ListModel("items")
     l.append_item("3")
     l.append_item("1")
     l.append_item("4")
     l.append_item("2")
     l2 = ListModel.FilteredListModel(container=l, items_key="items")
     l2.sort_key = lambda x: x
     l2.mark_changed()
     self.assertEqual(["1", "2", "3", "4"], l2.items)
     l.remove_item(1)
     l.remove_item(1)
     self.assertEqual(["2", "3"], l2.items)
     l.insert_item(0, "5")
     l.insert_item(0, "1")
     self.assertEqual(["1", "2", "3", "5"], l2.items)
Exemple #18
0
 def test_filter_model_inits_with_source_model(self):
     list_model = ListModel.ListModel("data_items")
     filtered_data_items = ListModel.FilteredListModel(items_key="data_items")
     filtered_data_items.container = list_model
     filtered_data_items.sort_key = operator.attrgetter("title")
     data_items = list()
     for value in TestDataItemsModelModule.values:
         data_item = DataItem.DataItem(numpy.zeros((16, 16), numpy.uint32))
         data_item.title = value
         list_model.insert_item(0, data_item)
         data_items.append(data_item)
     self.assertEqual([d.title for d in filtered_data_items.items], sorted([d.title for d in filtered_data_items.items]))
     selection = Selection.IndexedSelection()
     filtered_data_items2 = ListModel.FilteredListModel(items_key="data_items", container=filtered_data_items, selection=selection)
     self.assertEqual([d.title for d in filtered_data_items.items], [d.title for d in filtered_data_items2.items])
     for data_item in data_items:
         data_item.close()
Exemple #19
0
    def test_table_widget_handles_pending_updates_in_close(self):
        from nion.ui import Widgets
        ui = TestUI.UserInterface()

        def create_item(item):
            return ui.create_label_widget(item)

        widget = Widgets.TableWidget(ui, create_item)
        list_model = ListModel.ListModel()
        widget.bind_items(Binding.ListBinding(list_model, "items"))
        with contextlib.closing(widget):
            list_model.insert_item(0, "abc")
            list_model.insert_item(1, "abc")
            list_model.remove_item(0)
            self.assertEqual(3, len(widget.pending_queued_tasks))
        self.assertEqual(0, len(widget.pending_queued_tasks))
        widget.run_pending_keyed_tasks()
Exemple #20
0
 def test_inserting_items_into_model_index0_without_sort_key__but_with_filter_puts_them_in_same_order(self):
     values = ["DEF", "ABC", "GHI", "DFG", "ACD", "GIJ"]
     indexes = [0, 0, 1, 1, 2, 4]
     result = ["ABC", "DFG", "ACD", "GHI", "GIJ", "DEF"]
     list_model = ListModel.ListModel("data_items")
     filtered_data_items = ListModel.FilteredListModel(items_key="data_items")
     filtered_data_items.container = list_model
     filtered_data_items.filter = ListModel.NotFilter(ListModel.StartsWithFilter("title", "D"))
     data_items = list()
     for index, value in enumerate(values):
         data_item = DataItem.DataItem(numpy.zeros((16, 16), numpy.uint32))
         data_item.title = value
         list_model.insert_item(indexes[index], data_item)
         data_items.append(data_item)
     self.assertEqual([d.title for d in filtered_data_items.items], [v for v in result if not v.startswith("D")])
     for data_item in data_items:
         data_item.close()
Exemple #21
0
 def test_filtered_list_unsorted_retains_order(self):
     l = ListModel.ListModel("items")
     l.append_item("3")
     l.append_item("1")
     l.append_item("4")
     l.append_item("2")
     l2 = ListModel.FilteredListModel(container=l, items_key="items")
     l2.filter = ListModel.PredicateFilter(lambda x: x != "4")
     self.assertEqual(["3", "1", "2"], l2.items)
     l.remove_item(0)
     self.assertEqual(["1", "2"], l2.items)
     l.insert_item(0, "3")
     l.append_item("44")
     self.assertEqual(["3", "1", "2", "44"], l2.items)
     l2.begin_change()
     l.insert_item(0, "5")
     l.append_item("6")
     l2.end_change()
     self.assertEqual(["5", "3", "1", "2", "44", "6"], l2.items)
Exemple #22
0
    def __init__(self) -> None:
        super().__init__()

        # define our list of shapes, one from each class
        self.shapes_model = ListModel.ListModel(items=[Rectangle(), Circle(), Interval()])

        # define the shape index model
        self.shape_index_model = Model.PropertyModel(0)

        # define the shape page model
        self.shape_page = Model.PropertyModel[str]()

        def shape_index_changed(p: typing.Optional[str] = None) -> None:
            self.shape_page.value = self.shapes_model.items[self.shape_index_model.value or 0].type

        shape_index_changed()

        # track changes to shape index and update shape page.
        self.__shape_index_changed = self.shape_index_model.property_changed_event.listen(shape_index_changed)
Exemple #23
0
    def test_inserting_and_removing_item_into_binding_notifies_target(self):
        list_model = ListModel.ListModel("items")
        binding = Binding.ListBinding(list_model, "items")
        list_copy = list()

        def inserter(value: str, index: int) -> None:
            list_copy.insert(index, value)

        def remover(index: int) -> None:
            del list_copy[index]

        binding.inserter = inserter
        binding.remover = remover
        list_model.insert_item(0, "zero")
        list_model.insert_item(1, "one")
        self.assertEqual(len(list_copy), 2)
        self.assertEqual(list_copy[1], "one")
        list_model.remove_item(0)
        self.assertEqual(len(list_copy), 1)
        self.assertEqual(list_copy[0], "one")
 def __init__(self) -> None:
     super().__init__()
     # define our list of shapes, one from each class
     self.shapes_model = ListModel.ListModel(
         items=[Rectangle(), Circle(), Interval()])
Exemple #25
0
    def create_panel_widget(self, ui: Facade.UserInterface, document_controller: Facade.DocumentWindow) -> Facade.ColumnWidget:
        stem_controller_ = typing.cast(stem_controller.STEMController, Registry.get_component("stem_controller"))

        self.__scan_hardware_source_choice = HardwareSourceChoice.HardwareSourceChoice(ui._ui, "scan_acquisition_hardware_source_id", lambda hardware_source: hardware_source.features.get("is_scanning"))
        self.__camera_hardware_source_choice = HardwareSourceChoice.HardwareSourceChoice(ui._ui, "scan_acquisition_camera_hardware_source_id", lambda hardware_source: hardware_source.features.get("is_camera"))
        self.__scan_acquisition_preference_panel = ScanAcquisitionPreferencePanel(self.__scan_hardware_source_choice, self.__camera_hardware_source_choice)

        self.__scan_hardware_source_stream = HardwareSourceChoice.HardwareSourceChoiceStream(self.__scan_hardware_source_choice).add_ref()
        self.__camera_hardware_source_stream = HardwareSourceChoice.HardwareSourceChoiceStream(self.__camera_hardware_source_choice).add_ref()

        def update_context() -> None:
            scan_hardware_source = typing.cast(scan_base.ScanHardwareSource, self.__scan_hardware_source_choice.hardware_source)
            scan_context = scan_hardware_source.scan_context

            if scan_context.is_valid and scan_hardware_source.line_scan_enabled and scan_hardware_source.line_scan_vector:
                calibration = scan_context.calibration
                start = Geometry.FloatPoint.make(scan_hardware_source.line_scan_vector[0])
                end = Geometry.FloatPoint.make(scan_hardware_source.line_scan_vector[1])
                length = int(Geometry.distance(start, end) * scan_context.size.height)
                max_dim = max(scan_context.size.width, scan_context.size.height)
                length_str = calibration.convert_to_calibrated_size_str(length, value_range=(0, max_dim), samples=max_dim)
                line_str = _("Line Scan")
                self.__roi_description.text = f"{line_str} {length_str} ({length} px)"
                scan_str = _("Scan (1D)")
                scan_length = max(self.__scan_width, 1)
                self.__scan_label_widget.text = f"{scan_str} {scan_length} px"
                self.__scan_pixels = scan_length
                self.__scan_specifier.scan_context = copy.deepcopy(scan_context)
                self.__scan_specifier.size = 1, scan_length
                self.__scan_specifier.drift_interval_lines = 0
                self.__acquire_button._widget.enabled = True
            elif scan_context.is_valid and scan_hardware_source.subscan_enabled and scan_hardware_source.subscan_region:
                calibration = scan_context.calibration
                width = scan_hardware_source.subscan_region.width * scan_context.size.width
                height = scan_hardware_source.subscan_region.height * scan_context.size.height
                width_str = calibration.convert_to_calibrated_size_str(width, value_range=(0, scan_context.size.width), samples=scan_context.size.width)
                height_str = calibration.convert_to_calibrated_size_str(height, value_range=(0, scan_context.size.height), samples=scan_context.size.height)
                rect_str = _("Subscan")
                self.__roi_description.text = f"{rect_str} {width_str} x {height_str} ({int(width)} px x {int(height)} px)"
                scan_str = _("Scan (2D)")
                scan_width = self.__scan_width
                scan_height = int(self.__scan_width * height / width)
                drift_lines = scan_hardware_source.calculate_drift_lines(scan_width, self.__exposure_time_ms_value_model.value / 1000) if scan_hardware_source else 0
                drift_str = f" / Drift {drift_lines} lines" if drift_lines > 0 else str()
                self.__scan_label_widget.text = f"{scan_str} {scan_width} x {scan_height} px" + drift_str
                self.__scan_pixels = scan_width * scan_height
                self.__scan_specifier.scan_context = copy.deepcopy(scan_context)
                self.__scan_specifier.size = scan_height, scan_width
                self.__scan_specifier.drift_interval_lines = drift_lines
                self.__acquire_button._widget.enabled = True
            elif scan_context.is_valid:
                calibration = scan_context.calibration
                width = scan_context.size.width
                height = scan_context.size.height
                width_str = calibration.convert_to_calibrated_size_str(width, value_range=(0, scan_context.size.width), samples=scan_context.size.width)
                height_str = calibration.convert_to_calibrated_size_str(height, value_range=(0, scan_context.size.height), samples=scan_context.size.height)
                data_str = _("Context Scan")
                self.__roi_description.text = f"{data_str} {width_str} x {height_str} ({int(width)} x {int(height)})"
                scan_str = _("Scan (2D)")
                scan_width = self.__scan_width
                scan_height = int(self.__scan_width * height / width)
                drift_lines = scan_hardware_source.calculate_drift_lines(scan_width, self.__exposure_time_ms_value_model.value / 1000) if scan_hardware_source else 0
                drift_str = f" / Drift {drift_lines} lines" if drift_lines > 0 else str()
                self.__scan_label_widget.text = f"{scan_str} {scan_width} x {scan_height} px" + drift_str
                self.__scan_pixels = scan_width * scan_height
                self.__scan_specifier.scan_context = copy.deepcopy(scan_context)
                self.__scan_specifier.size = scan_height, scan_width
                self.__scan_specifier.drift_interval_lines = drift_lines
                self.__acquire_button._widget.enabled = True
            else:
                self.__roi_description.text = _("Scan context not active")
                self.__scan_label_widget.text = None
                self.__scan_specifier.scan_context = stem_controller.ScanContext()
                self.__scan_specifier.size = None
                self.__scan_specifier.drift_interval_lines = 0
                self.__acquire_button._widget.enabled = self.__acquisition_state == SequenceState.scanning  # focus will be on the SI data, so enable if scanning
                self.__scan_pixels = 0

            self.__scan_width_widget.text = Converter.IntegerToStringConverter().convert(self.__scan_width)

            self.__update_estimate()

        def stem_controller_property_changed(key: str) -> None:
            if key in ("subscan_state", "subscan_region", "subscan_rotation", "line_scan_state", "line_scan_vector", "drift_channel_id", "drift_region", "drift_settings"):
                document_controller._document_controller.event_loop.call_soon_threadsafe(update_context)

        def scan_context_changed() -> None:
            update_context()

        self.__stem_controller_property_listener = None
        self.__scan_context_changed_listener = None

        if stem_controller_:
            self.__stem_controller_property_listener = stem_controller_.property_changed_event.listen(stem_controller_property_changed)
            self.__scan_context_changed_listener = stem_controller_.scan_context_changed_event.listen(scan_context_changed)

        column = ui.create_column_widget()

        self.__styles_list_model = ListModel.ListModel(items=[ScanAcquisitionProcessing.SUM_PROJECT, ScanAcquisitionProcessing.NONE])
        self.__styles_list_property_model = ListModel.ListPropertyModel(self.__styles_list_model)
        self.__style_combo_box = ui.create_combo_box_widget(self.__styles_list_property_model.value, item_text_getter=operator.attrgetter("value.display_name"))
        self.__style_combo_box._widget.set_property("min-width", 100)
        items_binding = Binding.PropertyBinding(self.__styles_list_property_model, "value")
        items_binding.source_setter = None
        self.__style_combo_box._widget.bind_items(items_binding)
        self.__style_combo_box.current_index = 0

        self.__acquire_button = ui.create_push_button_widget(_("Acquire"))

        self.__roi_description = ui.create_label_widget()

        self.__scan_width_widget = ui.create_line_edit_widget()

        self.__exposure_time_widget = ui.create_line_edit_widget()

        self.__estimate_label_widget = ui.create_label_widget()

        self.__scan_label_widget = ui.create_label_widget()

        class ComboBoxWidget:
            def __init__(self, widget):
                self.__combo_box_widget = widget

            @property
            def _widget(self):
                return self.__combo_box_widget

        camera_row = ui.create_row_widget()
        camera_row.add_spacing(12)
        camera_row.add(ComboBoxWidget(self.__camera_hardware_source_choice.create_combo_box(ui._ui)))
        camera_row.add_spacing(12)
        camera_row.add(self.__style_combo_box)
        camera_row.add_spacing(12)
        camera_row.add_stretch()

        scan_choice_row = ui.create_row_widget()
        scan_choice_row.add_spacing(12)
        scan_choice_row.add(ComboBoxWidget(self.__scan_hardware_source_choice.create_combo_box(ui._ui)))
        scan_choice_row.add_spacing(12)
        scan_choice_row.add_stretch()

        roi_size_row = ui.create_row_widget()
        roi_size_row.add_spacing(12)
        roi_size_row.add(self.__roi_description)
        roi_size_row.add_spacing(12)
        roi_size_row.add_stretch()

        scan_spacing_pixels_row = ui.create_row_widget()
        scan_spacing_pixels_row.add_spacing(12)
        scan_spacing_pixels_row.add(ui.create_label_widget("Scan Width (pixels)"))
        scan_spacing_pixels_row.add_spacing(12)
        scan_spacing_pixels_row.add(self.__scan_width_widget)
        scan_spacing_pixels_row.add_spacing(12)
        scan_spacing_pixels_row.add_stretch()

        eels_exposure_row = ui.create_row_widget()
        eels_exposure_row.add_spacing(12)
        eels_exposure_row.add(ui.create_label_widget("Camera Exposure Time (ms)"))
        eels_exposure_row.add_spacing(12)
        eels_exposure_row.add(self.__exposure_time_widget)
        eels_exposure_row.add_spacing(12)
        eels_exposure_row.add_stretch()

        scan_row = ui.create_row_widget()
        scan_row.add_spacing(12)
        scan_row.add(self.__scan_label_widget)
        scan_row.add_stretch()

        estimate_row = ui.create_row_widget()
        estimate_row.add_spacing(12)
        estimate_row.add(self.__estimate_label_widget)
        estimate_row.add_stretch()

        acquire_sequence_button_row = ui.create_row_widget()
        acquire_sequence_button_row.add(self.__acquire_button)
        acquire_sequence_button_row.add_stretch()

        if self.__scan_hardware_source_choice.hardware_source_count > 1:
            column.add_spacing(8)
            column.add(scan_choice_row)
        column.add_spacing(8)
        column.add(camera_row)
        column.add_spacing(8)
        column.add(roi_size_row)
        column.add_spacing(8)
        column.add(scan_spacing_pixels_row)
        column.add_spacing(8)
        column.add(eels_exposure_row)
        column.add_spacing(8)
        column.add(scan_row)
        column.add_spacing(8)
        column.add(estimate_row)
        column.add_spacing(8)
        column.add(acquire_sequence_button_row)
        column.add_spacing(8)
        column.add_stretch()

        def camera_hardware_source_changed(hardware_source):
            self.disconnect_camera_hardware_source()
            if hardware_source:
                self.connect_camera_hardware_source(hardware_source)
                if hardware_source.features.get("has_masked_sum_option"):
                    self.__styles_list_model.items = [ScanAcquisitionProcessing.SUM_PROJECT, ScanAcquisitionProcessing.NONE, ScanAcquisitionProcessing.SUM_MASKED]
                else:
                    self.__styles_list_model.items = [ScanAcquisitionProcessing.SUM_PROJECT, ScanAcquisitionProcessing.NONE]

        self.__camera_hardware_changed_event_listener = self.__camera_hardware_source_choice.hardware_source_changed_event.listen(camera_hardware_source_changed)
        camera_hardware_source_changed(self.__camera_hardware_source_choice.hardware_source)

        def style_current_item_changed(current_item):
            self.__update_estimate()

        self.__style_combo_box.on_current_item_changed = style_current_item_changed

        def scan_width_changed(text: str) -> None:
            scan_width = Converter.IntegerToStringConverter().convert_back(text) if text else 1
            scan_width = max(scan_width, 1)
            if scan_width != self.__scan_width:
                self.__scan_width = scan_width
                update_context()
            self.__scan_width_widget.request_refocus()

        self.__scan_width_widget.on_editing_finished = scan_width_changed

        def acquisition_state_changed(acquisition_state: SequenceState) -> None:
            self.__acquisition_state = acquisition_state

            async def update_button_text(text: str) -> None:
                self.__acquire_button.text = text
                update_context()  # update the cancel button

            if acquisition_state == SequenceState.idle:
                self.__scan_acquisition_controller = None
                self.__acquisition_state_changed_event_listener.close()
                self.__acquisition_state_changed_event_listener = None
                document_controller._document_window.event_loop.create_task(update_button_text(_("Acquire")))
            else:
                document_controller._document_window.event_loop.create_task(update_button_text(_("Cancel")))

        def acquire_sequence() -> None:
            if self.__scan_acquisition_controller:
                if self.__scan_acquisition_controller:
                    self.__scan_acquisition_controller.cancel()
            else:
                if self.__scan_hardware_source_choice.hardware_source:
                    scan_hardware_source = self.__api.get_hardware_source_by_id(self.__scan_hardware_source_choice.hardware_source.hardware_source_id, version="1.0")
                else:
                    scan_hardware_source = None

                if self.__camera_hardware_source_choice.hardware_source:
                    camera_hardware_source = self.__api.get_hardware_source_by_id(self.__camera_hardware_source_choice.hardware_source.hardware_source_id, version="1.0")
                else:
                    camera_hardware_source = None

                if scan_hardware_source and camera_hardware_source:
                    self.__scan_acquisition_controller = ScanAcquisitionController(self.__api, document_controller, scan_hardware_source, camera_hardware_source, self.__scan_specifier)
                    self.__acquisition_state_changed_event_listener = self.__scan_acquisition_controller.acquisition_state_changed_event.listen(acquisition_state_changed)
                    self.__scan_acquisition_controller.start(self.__style_combo_box.current_item)

        self.__acquire_button.on_clicked = acquire_sequence

        self.__update_estimate()

        update_context()

        return column