def test_add_item_to_string_list_widget_causes_container_to_relayout(self): from nion.ui import Widgets ui = TestUI.UserInterface() widget = Widgets.StringListWidget(ui) with contextlib.closing(widget): canvas_item = widget.content_widget.children[0].canvas_item canvas_item.update_layout(Geometry.IntPoint(x=0, y=0), Geometry.IntSize(width=300, height=200), immediate=True) scroll_area_canvas_item = canvas_item.canvas_items[0].canvas_items[ 0] canvas_item.layout_immediate( Geometry.IntSize(width=300, height=200)) # check assumptions self.assertEqual(scroll_area_canvas_item.canvas_rect.height, 200) self.assertEqual( scroll_area_canvas_item.content.canvas_rect.height, 0) # add item self.assertFalse(canvas_item._needs_layout_for_testing) widget.items = ["abc"] # self.assertTrue(canvas_item._needs_layout_for_testing) # check that column was laid out again canvas_item.layout_immediate(Geometry.IntSize(width=300, height=200), force=False) self.assertEqual(scroll_area_canvas_item.canvas_rect.height, 200) self.assertEqual( scroll_area_canvas_item.content.canvas_rect.height, 20)
def __init__(self, window: Window.Window, profile: Profile.Profile): content_widget = window.ui.create_column_widget() super().__init__(content_widget) ui = window.ui self._tree_model = TreeModel(profile) def closed_items_changed(closed_items: typing.Set[str]) -> None: profile.closed_items = list(closed_items) self._tree_model.on_closed_items_changed = closed_items_changed self._tree_selection = Selection.IndexedSelection( Selection.Style.multiple) projects_list_widget = Widgets.ListWidget( ui, ProjectTreeCanvasItemDelegate(window, self._tree_model), selection=self._tree_selection, v_scroll_enabled=False, v_auto_resize=True) projects_list_widget.wants_drag_events = True projects_list_widget.bind_items( Binding.PropertyBinding(self._tree_model, "value")) projects_section = Widgets.SectionWidget(ui, _("Projects"), projects_list_widget) projects_section.expanded = True content_widget.add(projects_section) # configure an observer for watching for project references changes. # this serves as the master updater for changes. move to document controller? def project_references_changed(item: Observer.ItemValue) -> None: # update the tree model. project_references = typing.cast( typing.Sequence[Profile.ProjectReference], item) self._tree_model.update_project_references(project_references) oo = Observer.ObserverBuilder() oo.source(profile).ordered_sequence_from_array( "project_references").collect_list().action_fn( project_references_changed) self.__projects_model_observer = typing.cast( Observer.AbstractItemSource, oo.make_observable())
def test_add_item_to_string_list_widget_causes_container_to_relayout( self) -> None: # ugly type casting from nion.ui import Widgets ui = TestUI.UserInterface() widget = Widgets.StringListWidget(ui) with contextlib.closing(widget): canvas_item = typing.cast( CanvasItem.CanvasItemComposition, typing.cast( UserInterface.CanvasWidget, typing.cast( UserInterface.BoxWidget, widget.content_widget).children[0]).canvas_item) canvas_item.update_layout(Geometry.IntPoint(x=0, y=0), Geometry.IntSize(width=300, height=200), immediate=True) scroll_area_canvas_item = typing.cast( CanvasItem.ScrollAreaCanvasItem, typing.cast(CanvasItem.CanvasItemComposition, canvas_item.canvas_items[0]).canvas_items[0]) canvas_item.layout_immediate( Geometry.IntSize(width=300, height=200)) # check assumptions scroll_canvas_rect = scroll_area_canvas_item.canvas_rect or Geometry.IntRect.empty_rect( ) scroll_content = scroll_area_canvas_item.content assert scroll_content self.assertEqual(scroll_canvas_rect.height, 200) scroll_content_rect = scroll_content.canvas_rect or Geometry.IntRect.empty_rect( ) self.assertEqual(scroll_content_rect.height, 0) # add item self.assertFalse(canvas_item._needs_layout_for_testing) widget.items = ["abc"] # self.assertTrue(canvas_item._needs_layout_for_testing) # check that column was laid out again canvas_item.layout_immediate(Geometry.IntSize(width=300, height=200), force=False) scroll_canvas_rect = scroll_area_canvas_item.canvas_rect or Geometry.IntRect.empty_rect( ) scroll_content = scroll_area_canvas_item.content assert scroll_content scroll_content_rect = scroll_content.canvas_rect or Geometry.IntRect.empty_rect( ) self.assertEqual(scroll_canvas_rect.height, 200) self.assertEqual(scroll_content_rect.height, 20)
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()
def __init__(self, ui, app): super().__init__(ui, _("Preferences"), app=app) self.ui = ui self.document_controller = self self._create_menus() properties = dict() properties["min-height"] = 400 properties["min-width"] = 800 preference_pane_delegates = list() preference_pane_delegate_id_ref = [None] content_stack = ui.create_stack_widget() def change_selection(indexes): index = list(indexes)[0] assert 0 <= index < len(preference_pane_delegates) content_stack.current_index = index preference_pane_delegate_id_ref[0] = preference_pane_delegates[ index].identifier selector_list_widget = Widgets.StringListWidget( ui, selection_style=Selection.Style.single_or_none) selector_list_widget.on_selection_changed = change_selection row = self.ui.create_row_widget(properties={ "min-width": 640, "min-height": 320 }) selector_column = self.ui.create_column_widget( properties={"width": 200}) selector_row = ui.create_row_widget() selector_row.add_spacing(8) selector_row.add(selector_list_widget) selector_row.add_spacing(8) selector_column.add_spacing(8) selector_column.add(selector_row) selector_column.add_spacing(8) content_column = self.ui.create_column_widget() content_column.add(content_stack) row.add(selector_column) row.add(content_column) self.content.add(row) self.add_button(_("Done"), lambda: True) def rebuild(): content_stack.remove_all() preference_pane_delegates.clear() preference_pane_delegate_id = preference_pane_delegate_id_ref[0] items = list() selected_index = 0 for index, preference_pane_delegate in enumerate( PreferencesManager().preference_pane_delegates): preference_pane_delegates.append(preference_pane_delegate) content_column_widget = ui.create_column_widget() content_column_widget.add_spacing(12) content_column_widget.add( preference_pane_delegate.build(ui, event_loop=self.event_loop)) content_column_widget.add_spacing(12) content_row_widget = ui.create_row_widget() content_row_widget.add_spacing(12) content_row_widget.add(content_column_widget) content_row_widget.add_spacing(12) content_stack.add(content_row_widget) items.append(preference_pane_delegate.label) if preference_pane_delegate.identifier == preference_pane_delegate_id: selected_index = index change_selection({selected_index}) selector_list_widget.items = items self.__preference_pane_delegates_changed_listener = PreferencesManager( ).preference_pane_delegates_changed_event.listen(rebuild) rebuild()
def __init__(self, document_controller: "DocumentController.DocumentController"): ui = document_controller.ui super().__init__(ui, _("Scripts"), parent_window=document_controller, persistent_id="ScriptsDialog") self.ui = ui self.document_controller = document_controller app = typing.cast(typing.Any, self.document_controller.app) # trick typing self.__profile: typing.Optional[ Profile.Profile] = app.profile if app else None self.script_filter_pattern = "\\.py$" self.__cancelled = False self.__thread = None properties = dict() properties["min-height"] = 180 properties["min-width"] = 540 self.__output_widget = self.ui.create_text_edit_widget(properties) self.__output_widget.set_text_font( Panel.Panel.get_monospace_text_font()) self.__output_widget.set_line_height_proportional( Panel.Panel.get_monospace_proportional_line_height()) self.__message_column = ui.create_column_widget() self.__message_column.add(self.__make_cancel_row()) # load the list of script items items = [] if self.__profile: for script_item in self.__profile.script_items: if isinstance(script_item, Profile.FileScriptItem): script_list_item = ScriptListItem(str(script_item.path)) script_list_item.script_item = script_item items.append(script_list_item) elif isinstance(script_item, Profile.FolderScriptItem): folder_list_item = FolderListItem( str(script_item.folder_path)) folder_list_item.script_item = script_item folder_list_item.folder_closed = script_item.is_closed items.append(folder_list_item) self.__new_path_entries = [] for item in items: if isinstance(item, FolderListItem): full_path = item.full_path self.__new_path_entries.append(full_path) if full_path not in sys.path: sys.path.append(full_path) def selected_changed(indexes: typing.AbstractSet[int]) -> None: run_button_widget.enabled = len(indexes) == 1 def add_clicked() -> None: assert self.__profile add_dir = self.ui.get_persistent_string("import_directory", "") file_paths, filter_str, directory = self.get_file_paths_dialog( _("Add Scripts"), add_dir, "Python Files (*.py)", "Python Files (*.py)") self.ui.set_persistent_string("import_directory", directory) items = self.scripts_list_widget.items for file_path_str in file_paths: script_item = Profile.FileScriptItem( pathlib.Path(file_path_str)) self.__profile.append_script_item(script_item) script_list_item = ScriptListItem(file_path_str) script_list_item.script_item = script_item items.append(script_list_item) self.update_scripts_list(items) def add_folder_clicked() -> None: assert self.__profile add_dir = self.ui.get_persistent_string("import_directory", "") existing_directory, directory = self.ui.get_existing_directory_dialog( _("Add Scripts Folder"), add_dir) if existing_directory: folder_list_item = FolderListItem(existing_directory) folder_list_item.update_content_from_file_system( filter_pattern=self.script_filter_pattern) full_path = folder_list_item.full_path if full_path not in sys.path: sys.path.append(full_path) self.__new_path_entries.append(full_path) items = self.scripts_list_widget.items script_item = Profile.FolderScriptItem( pathlib.Path(existing_directory)) self.__profile.append_script_item(script_item) folder_list_item.script_item = script_item items.append(folder_list_item) self.update_scripts_list(items) else: self.rebuild_scripts_list() def remove_clicked() -> None: assert self.__profile indexes = list(self.scripts_list_widget.selected_items) new_items = [] for i, item in enumerate(self.scripts_list_widget.items): if i not in indexes: new_items.append(item) elif item.script_item: self.__profile.remove_script_item(item.script_item) self.update_scripts_list(new_items) def run_clicked() -> None: indexes = self.scripts_list_widget.selected_items if len(indexes) == 1: script_item = self.scripts_list_widget.items[list(indexes)[0]] script_item.check_existence() # Use "type" instead of "isinstance" to exclude subclasses from matching if type(script_item) is ScriptListItem and script_item.exists: script_path = script_item.full_path self.run_script(script_path) def item_selected(index: int) -> bool: run_clicked() return True self.scripts_list_widget = Widgets.ListWidget( ui, ScriptListCanvasItemDelegate(ui, document_controller, self.rebuild_scripts_list), items=items, selection_style=Selection.Style.single_or_none, border_color="#888", properties={ "min-height": 200, "min-width": 560, "size-policy-vertical": "expanding" }) self.scripts_list_widget.on_selection_changed = selected_changed self.scripts_list_widget.on_item_selected = item_selected self.rebuild_scripts_list() add_button_widget = ui.create_push_button_widget(_("Add...")) add_button_widget.on_clicked = add_clicked add_folder_button_widget = ui.create_push_button_widget( _("Add Folder...")) add_folder_button_widget.on_clicked = add_folder_clicked remove_button_widget = ui.create_push_button_widget(_("Remove")) remove_button_widget.on_clicked = remove_clicked run_button_widget = ui.create_push_button_widget(_("Run")) run_button_widget.on_clicked = run_clicked run_button_widget.enabled = False list_widget_row = ui.create_row_widget() list_widget_row.add_spacing(8) list_widget_row.add(self.scripts_list_widget) list_widget_row.add_spacing(8) close_button_widget = ui.create_push_button_widget(_("Close")) close_button_widget.on_clicked = self.request_close button_row = ui.create_row_widget() button_row.add_spacing(12) button_row.add(add_button_widget) button_row.add_spacing(4) button_row.add(add_folder_button_widget) button_row.add_spacing(4) button_row.add(remove_button_widget) button_row.add_stretch() button_row.add(run_button_widget) button_row.add_spacing(12) select_column = ui.create_column_widget() select_column.add_spacing(8) select_column.add(list_widget_row) select_column.add_spacing(8) select_column.add(button_row) select_column.add_spacing(8) run_column = ui.create_column_widget() run_column.add(self.__output_widget) run_column.add_spacing(6) run_column.add(self.__message_column) self.__stack = ui.create_stack_widget() self.__stack.add(select_column) self.__stack.add(run_column) self.content.add(self.__stack) self.__sync_events: typing.Set[threading.Event] = set() self.__lock = threading.RLock() self.__q: typing.Deque[typing.Callable[[], None]] = collections.deque() self.__output_queue: typing.Deque[str] = collections.deque() self.__is_closed = False
def __init__(self, ui, app): super().__init__(ui, _("Choose Library")) current_item_ref = [None] def handle_choose(): current_item = current_item_ref[0] if current_item: app.switch_library(current_item) return True return False def handle_new(): workspace_dir = pose_open_library_dialog_fn() if workspace_dir: items.insert(0, (workspace_dir, datetime.datetime.now())) list_widget.items = items list_widget.set_selected_index(0) app.switch_library(workspace_dir) return True return False self.add_button(_("New..."), handle_new) self.add_button(_("Other..."), handle_new) self.add_button(_("Cancel"), lambda: True) self.add_button(_("Choose"), handle_choose) path_label = ui.create_label_widget() prompt_row = ui.create_row_widget() prompt_row.add_spacing(13) prompt_row.add(ui.create_label_widget(_("Which library do you want Nion Swift to use?"), properties={"stylesheet": "font-weight: bold"})) prompt_row.add_spacing(13) prompt_row.add_stretch() explanation1_row = ui.create_row_widget() explanation1_row.add_spacing(13) explanation1_row.add(ui.create_label_widget(_("You can select a library from the list, find another library, or create a new library."))) explanation1_row.add_spacing(13) explanation1_row.add_stretch() explanation2_row = ui.create_row_widget() explanation2_row.add_spacing(13) explanation2_row.add(ui.create_label_widget(_("The same library will be used the next time you open Nion Swift."))) explanation2_row.add_spacing(13) explanation2_row.add_stretch() def selection_changed(indexes): if len(indexes) == 1: item = items[list(indexes)[0]] current_item_ref[0] = os.path.dirname(item[0]) path_label.text = os.path.dirname(item[0]) else: current_item_ref[0] = None path_label.text = None def stringify_item(item): date_utc = item[1] tz_minutes = Utility.local_utcoffset_minutes(date_utc) date_local = date_utc + datetime.timedelta(minutes=tz_minutes) return str(os.path.basename(os.path.dirname(item[0]))) + " (" + date_local.strftime("%c") + ")" def item_selected(index): item = items[index] current_item_ref[0] = os.path.dirname(item[0]) path_label.text = os.path.dirname(item[0]) handle_choose() self.request_close() list_widget = Widgets.StringListWidget(ui, items=items, selection_style=Selection.Style.single_or_none, item_getter=stringify_item, border_color="#888", properties={"min-height": 200, "min-width": 560}) list_widget.on_selection_changed = selection_changed list_widget.on_item_selected = item_selected list_widget.on_cancel = self.request_close if len(items) > 0: list_widget.set_selected_index(0) items_row = ui.create_row_widget() items_row.add_spacing(13) items_row.add(list_widget) items_row.add_spacing(13) items_row.add_stretch() path_row = ui.create_row_widget() path_row.add_spacing(13) path_row.add(path_label) path_row.add_spacing(13) path_row.add_stretch() column = ui.create_column_widget() column.add_spacing(18) column.add(prompt_row) column.add_spacing(6) column.add(explanation1_row) column.add(explanation2_row) column.add_spacing(12) column.add(items_row) column.add_spacing(6) column.add(path_row) column.add_spacing(6) column.add_stretch() self.content.add(column) self.__list_widget = list_widget
def __init__(self, document_controller): ui = document_controller.ui super().__init__(ui, _("Interactive Dialog"), document_controller.app, persistent_id="ScriptsDialog") self.ui = ui self.document_controller = document_controller self._create_menus() self.__cancelled = False self.__thread = None properties = dict() properties["min-height"] = 180 properties["min-width"] = 540 properties[ "stylesheet"] = "background: white; font-family: Monaco, Courier, monospace" self.__output_widget = self.ui.create_text_edit_widget(properties) self.__message_column = ui.create_column_widget() self.__message_column.add(self.__make_cancel_row()) items = self.ui.get_persistent_object("interactive_scripts_0", list()) def selected_changed(indexes: AbstractSet[int]) -> None: run_button_widget.enabled = len(indexes) == 1 def add_clicked() -> None: add_dir = self.ui.get_persistent_string("import_directory", "") file_paths, filter_str, directory = self.get_file_paths_dialog( _("Add Scripts"), add_dir, "Python Files (*.py)", "Python Files (*.py)") self.ui.set_persistent_string("import_directory", directory) items.extend(file_paths) items.sort() list_widget.items = items self.ui.set_persistent_object("interactive_scripts_0", items) def remove_clicked() -> None: indexes = list(list_widget.selected_items) for index in sorted(indexes, reverse=True): del items[index] list_widget.items = items self.ui.set_persistent_object("interactive_scripts_0", items) def run_clicked() -> None: indexes = list_widget.selected_items if len(indexes) == 1: script_path = items[list(indexes)[0]] self.run_script(script_path) def item_selected(index: int) -> bool: run_clicked() return True list_widget = Widgets.StringListWidget( ui, items=items, selection_style=Selection.Style.single_or_none, border_color="#888", properties={ "min-height": 200, "min-width": 560 }) list_widget.on_selection_changed = selected_changed list_widget.on_item_selected = item_selected add_button_widget = ui.create_push_button_widget(_("Add...")) add_button_widget.on_clicked = add_clicked remove_button_widget = ui.create_push_button_widget(_("Remove")) remove_button_widget.on_clicked = remove_clicked run_button_widget = ui.create_push_button_widget(_("Run")) run_button_widget.on_clicked = run_clicked run_button_widget.enabled = False list_widget_row = ui.create_row_widget() list_widget_row.add_spacing(8) list_widget_row.add(list_widget) list_widget_row.add_spacing(8) close_button_widget = ui.create_push_button_widget(_("Close")) close_button_widget.on_clicked = self.request_close button_row = ui.create_row_widget() button_row.add_spacing(12) button_row.add(add_button_widget) button_row.add(remove_button_widget) button_row.add_stretch() button_row.add(run_button_widget) button_row.add_spacing(12) select_column = ui.create_column_widget() select_column.add_spacing(8) select_column.add(list_widget_row) select_column.add_spacing(8) select_column.add(button_row) select_column.add_spacing(8) run_column = ui.create_column_widget() run_column.add(self.__output_widget) run_column.add_spacing(6) run_column.add(self.__message_column) self.__stack = ui.create_stack_widget() self.__stack.add(select_column) self.__stack.add(run_column) self.content.add(self.__stack) self.__lock = threading.RLock() self.__q = collections.deque() self.__output_queue = collections.deque()
def __init__( self, ui: UserInterface.UserInterface, document_controller: DocumentController.DocumentController ) -> None: content_widget = ui.create_column_widget() super().__init__(content_widget) document_model = document_controller.document_model all_items_controller = CollectionDisplayItemCounter( _("All"), None, "all", document_controller) persistent_items_controller = CollectionDisplayItemCounter( _("Persistent"), None, "persistent", document_controller) live_items_controller = CollectionDisplayItemCounter( _("Live"), None, "temporary", document_controller) latest_items_controller = CollectionDisplayItemCounter( _("Latest Session"), None, "latest-session", document_controller) self.__item_controllers = [ all_items_controller, persistent_items_controller, live_items_controller, latest_items_controller ] self.__data_group_controllers: typing.List[ CollectionDisplayItemCounter] = list() collection_selection = Selection.IndexedSelection( Selection.Style.single_or_none) collections_list_widget = Widgets.ListWidget( ui, CollectionListCanvasItemDelegate(collection_selection), selection=collection_selection, v_scroll_enabled=False, v_auto_resize=True) collections_list_widget.wants_drag_events = True def filter_changed(data_group: typing.Optional[DataGroup.DataGroup], filter_id: typing.Optional[str]) -> None: if data_group: for index, controller in enumerate( collections_list_widget.items): if data_group == controller.data_group: collection_selection.set(index) break else: if filter_id == "latest-session": collection_selection.set(3) elif filter_id == "temporary": collection_selection.set(2) elif filter_id == "persistent": collection_selection.set(1) else: collection_selection.set(0) self.__filter_changed_event_listener = document_controller.filter_changed_event.listen( filter_changed) def collections_changed(t: str) -> None: collections_list_widget.items = [ all_items_controller, persistent_items_controller, live_items_controller, latest_items_controller, ] + self.__data_group_controllers all_items_controller.on_title_changed = collections_changed persistent_items_controller.on_title_changed = collections_changed live_items_controller.on_title_changed = collections_changed latest_items_controller.on_title_changed = collections_changed def document_model_item_inserted(key: str, value: typing.Any, before_index: int) -> None: if key == "data_groups": data_group = value controller = CollectionDisplayItemCounter( data_group.title, data_group, None, document_controller) self.__data_group_controllers.insert(before_index, controller) controller.on_title_changed = collections_changed collections_changed(str()) def document_model_item_removed(key: str, value: typing.Any, index: int) -> None: if key == "data_groups": controller = self.__data_group_controllers.pop(index) controller.close() collections_changed(str()) self.__document_model_item_inserted_listener = document_model.item_inserted_event.listen( document_model_item_inserted) self.__document_model_item_removed_listener = document_model.item_removed_event.listen( document_model_item_removed) data_group, filter_id = document_controller.get_data_group_and_filter_id( ) filter_changed(data_group, filter_id) for index, data_group in enumerate(document_model.data_groups): document_model_item_inserted("data_groups", data_group, index) collections_changed(str()) def collections_selection_changed( indexes: typing.AbstractSet[int]) -> None: if len(indexes) == 0: controller = collections_list_widget.items[0] document_controller.set_filter(controller.filter_id) elif len(indexes) == 1: controller = collections_list_widget.items[list(indexes)[0]] if controller.is_smart_collection: document_controller.set_filter(controller.filter_id) document_controller.set_data_group(None) else: document_controller.set_filter(None) document_controller.set_data_group(controller.data_group) collections_list_widget.on_selection_changed = collections_selection_changed collections_column = ui.create_column_widget() collections_column.add(collections_list_widget) collections_section = Widgets.SectionWidget(ui, _("Collections"), collections_column) collections_section.expanded = True content_widget.add(collections_section) content_widget.add_stretch() # for testing self._collection_selection = collection_selection
def __init__(self, document_controller: "DocumentController.DocumentController"): ui = document_controller.ui super().__init__(ui, _("Interactive Dialog"), parent_window=document_controller, persistent_id="ScriptsDialog") self.ui = ui self.document_controller = document_controller self.script_filter_pattern = "\.py$" self._create_menus() self.__cancelled = False self.__thread = None properties = dict() properties["min-height"] = 180 properties["min-width"] = 540 self.__output_widget = self.ui.create_text_edit_widget(properties) self.__output_widget.set_text_font( Panel.Panel.get_monospace_text_font()) self.__output_widget.set_line_height_proportional( Panel.Panel.get_monospace_proportional_line_height()) self.__message_column = ui.create_column_widget() self.__message_column.add(self.__make_cancel_row()) # Load the list of known scripts. Upgrade from old storage system ("interactive_scripts_0") items = [] items_str = self.ui.get_persistent_string("interactive_scripts_1", "") if items_str: for item_dict in json.loads(items_str): items.append(_create_list_item_from_dict(item_dict)) else: items_old = self.ui.get_persistent_object("interactive_scripts_0", list()) for item in items_old: items.append(ScriptListItem(item)) if items: items = _build_sorted_scripts_list(items) self.ui.set_persistent_string( "interactive_scripts_1", json.dumps([item.to_dict() for item in items])) self.__new_path_entries = [] for item in items: if isinstance(item, FolderListItem): full_path = item.full_path self.__new_path_entries.append(full_path) if not full_path in sys.path: sys.path.append(full_path) def selected_changed(indexes: AbstractSet[int]) -> None: run_button_widget.enabled = len(indexes) == 1 def add_clicked() -> None: add_dir = self.ui.get_persistent_string("import_directory", "") file_paths, filter_str, directory = self.get_file_paths_dialog( _("Add Scripts"), add_dir, "Python Files (*.py)", "Python Files (*.py)") self.ui.set_persistent_string("import_directory", directory) items = self.scripts_list_widget.items items.extend( [ScriptListItem(file_path) for file_path in file_paths]) self.update_scripts_list(items) def add_folder_clicked() -> None: add_dir = self.ui.get_persistent_string("import_directory", "") existing_directory, directory = self.ui.get_existing_directory_dialog( _("Add Scripts Folder"), add_dir) if existing_directory: new_folder = FolderListItem(existing_directory) new_folder.update_content_from_file_system( filter_pattern=self.script_filter_pattern) full_path = new_folder.full_path if not full_path in sys.path: sys.path.append(full_path) self.__new_path_entries.append(full_path) items = self.scripts_list_widget.items items.append(new_folder) self.update_scripts_list(items) else: self.rebuild_scripts_list() def remove_clicked() -> None: indexes = list(self.scripts_list_widget.selected_items) new_items = [] for i, item in enumerate(self.scripts_list_widget.items): if not i in indexes: new_items.append(item) self.update_scripts_list(new_items) def run_clicked() -> None: indexes = self.scripts_list_widget.selected_items if len(indexes) == 1: script_item = self.scripts_list_widget.items[list(indexes)[0]] script_item.check_existance() # Use "type" instead of "isinstance" to exclude subclasses from matching if type(script_item) is ScriptListItem and script_item.exists: script_path = script_item.full_path self.run_script(script_path) def item_selected(index: int) -> bool: run_clicked() return True self.scripts_list_widget = Widgets.ListWidget( ui, ScriptListCanvasItemDelegate(ui, document_controller, self.rebuild_scripts_list), items=items, selection_style=Selection.Style.single_or_none, border_color="#888", properties={ "min-height": 200, "min-width": 560, "size-policy-vertical": "expanding" }) self.scripts_list_widget.on_selection_changed = selected_changed self.scripts_list_widget.on_item_selected = item_selected self.rebuild_scripts_list() add_button_widget = ui.create_push_button_widget(_("Add...")) add_button_widget.on_clicked = add_clicked add_folder_button_widget = ui.create_push_button_widget( _("Add Folder...")) add_folder_button_widget.on_clicked = add_folder_clicked remove_button_widget = ui.create_push_button_widget(_("Remove")) remove_button_widget.on_clicked = remove_clicked run_button_widget = ui.create_push_button_widget(_("Run")) run_button_widget.on_clicked = run_clicked run_button_widget.enabled = False list_widget_row = ui.create_row_widget() list_widget_row.add_spacing(8) list_widget_row.add(self.scripts_list_widget) list_widget_row.add_spacing(8) close_button_widget = ui.create_push_button_widget(_("Close")) close_button_widget.on_clicked = self.request_close button_row = ui.create_row_widget() button_row.add_spacing(12) button_row.add(add_button_widget) button_row.add_spacing(4) button_row.add(add_folder_button_widget) button_row.add_spacing(4) button_row.add(remove_button_widget) button_row.add_stretch() button_row.add(run_button_widget) button_row.add_spacing(12) select_column = ui.create_column_widget() select_column.add_spacing(8) select_column.add(list_widget_row) select_column.add_spacing(8) select_column.add(button_row) select_column.add_spacing(8) run_column = ui.create_column_widget() run_column.add(self.__output_widget) run_column.add_spacing(6) run_column.add(self.__message_column) self.__stack = ui.create_stack_widget() self.__stack.add(select_column) self.__stack.add(run_column) self.content.add(self.__stack) self.__sync_events = set() self.__lock = threading.RLock() self.__q = collections.deque() self.__output_queue = collections.deque() self.__is_closed = False