Skip to content

Commit

Permalink
Add data info line to SVG export dialog.
Browse files Browse the repository at this point in the history
  • Loading branch information
lisham2000 committed Oct 4, 2024
1 parent 7a25d50 commit 41d6dec
Show file tree
Hide file tree
Showing 22 changed files with 1,136 additions and 1,766 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.11", "3.12", "3.13.0-rc.2"]
python-version: ["3.11", "3.12"]

steps:
- uses: actions/checkout@v4
Expand Down
173 changes: 51 additions & 122 deletions nion/swift/DataItemThumbnailWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from nion.ui import CanvasItem
from nion.ui import UserInterface
from nion.ui import Widgets
from nion.ui import CanvasItem
from nion.utils import Geometry
from nion.utils import Process
from nion.utils import ReferenceCounting
Expand Down Expand Up @@ -53,128 +52,71 @@ def populate_mime_data_for_drag(self, mime_data: UserInterface.MimeData, size: G
return False, None


class BitmapOverlayCanvasItemComposer(CanvasItem.BaseComposer):
def __init__(self, canvas_item: CanvasItem.AbstractCanvasItem, layout_sizing: CanvasItem.Sizing, cache: CanvasItem.ComposerCache, is_active: bool, is_dropping: bool, is_focused: bool) -> None:
super().__init__(canvas_item, layout_sizing, cache)
self.__is_active = is_active
self.__is_dropping = is_dropping
self.__is_focused = is_focused

def _repaint(self, drawing_context: DrawingContext.DrawingContext, canvas_bounds: Geometry.IntRect, composer_cache: CanvasItem.ComposerCache) -> None:
is_active = self.__is_active
is_dropping = self.__is_dropping
is_focused = self.__is_focused
focused_style = "#3876D6" # TODO: platform dependent
with drawing_context.saver():
drawing_context.translate(canvas_bounds.left, canvas_bounds.top)
if is_active:
with drawing_context.saver():
drawing_context.begin_path()
drawing_context.round_rect(2, 2, 6, 6, 3)
drawing_context.fill_style = "rgba(0, 255, 0, 0.80)"
drawing_context.fill()
if is_dropping:
with drawing_context.saver():
drawing_context.begin_path()
drawing_context.rect(0, 0, canvas_bounds.width, canvas_bounds.height)
drawing_context.fill_style = "rgba(255, 0, 0, 0.10)"
drawing_context.fill()
if is_focused:
stroke_style = focused_style
drawing_context.begin_path()
drawing_context.rect(2, 2, canvas_bounds.width - 4, canvas_bounds.height - 4)
drawing_context.line_join = "miter"
drawing_context.stroke_style = stroke_style
drawing_context.line_width = 4.0
drawing_context.stroke()


class BitmapOverlayCanvasItem(CanvasItem.AbstractCanvasItem):
def __init__(self) -> None:
super().__init__()
self.__is_active = False
self.__is_focused = False
self.__is_dropping = False

@property
def is_active(self) -> bool:
return self.__is_active

@is_active.setter
def is_active(self, value: bool) -> None:
if value != self.__is_active:
self.__is_active = value
self.update()

@property
def is_focused(self) -> bool:
return self.__is_focused

@is_focused.setter
def is_focused(self, value: bool) -> None:
if value != self.__is_focused:
self.__is_focused = value
self.update()

@property
def is_dropping(self) -> bool:
return self.__is_dropping

@is_dropping.setter
def is_dropping(self, value: bool) -> None:
if value != self.__is_dropping:
self.__is_dropping = value
self.update()

def _get_composer(self, composer_cache: CanvasItem.ComposerCache) -> typing.Optional[CanvasItem.BaseComposer]:
return BitmapOverlayCanvasItemComposer(self, self.layout_sizing, composer_cache, self.__is_active, self.__is_dropping, self.__is_focused)


class BitmapOverlayCanvasItemComposition(CanvasItem.CanvasItemComposition):
class BitmapOverlayCanvasItem(CanvasItem.CanvasItemComposition):

def __init__(self) -> None:
super().__init__()
self.focusable = True
self.__dropping = False
self.__focused = False
self.wants_drag_events = True
self.wants_mouse_events = True
self.__drag_start: typing.Optional[Geometry.IntPoint] = None
self.on_drop_mime_data: typing.Optional[typing.Callable[[UserInterface.MimeData, int, int], str]] = None
self.on_delete: typing.Optional[typing.Callable[[], None]] = None
self.on_drag_pressed: typing.Optional[typing.Callable[[int, int, UserInterface.KeyboardModifiers], None]] = None
self.__overlay_canvas_item = BitmapOverlayCanvasItem()
self.add_canvas_item(self.__overlay_canvas_item)
self.active = False

def close(self) -> None:
self.on_drop_mime_data = None
self.on_delete = None
self.on_drag_pressed = None
super().close()

@property
def active(self) -> bool:
return self.__overlay_canvas_item.is_active

@active.setter
def active(self, value: bool) -> None:
self.__overlay_canvas_item.is_active = value

@property
def focused(self) -> bool:
return self.__overlay_canvas_item.focused
return self.__focused

def _set_focused(self, focused: bool) -> None:
self.__overlay_canvas_item.is_focused = focused
if self.__focused != focused:
self.__focused = focused
self.update()

def update_sizing(self, new_sizing: CanvasItem.Sizing) -> None:
super().update_sizing(new_sizing)
self.__overlay_canvas_item.update_sizing(new_sizing)
def _repaint(self, drawing_context: DrawingContext.DrawingContext) -> None:
super()._repaint(drawing_context)
# canvas size
canvas_size = self.canvas_size
if canvas_size:
focused_style = "#3876D6" # TODO: platform dependent
if self.active:
with drawing_context.saver():
drawing_context.begin_path()
drawing_context.round_rect(2, 2, 6, 6, 3)
drawing_context.fill_style = "rgba(0, 255, 0, 0.80)"
drawing_context.fill()
if self.__dropping:
with drawing_context.saver():
drawing_context.begin_path()
drawing_context.rect(0, 0, canvas_size.width, canvas_size.height)
drawing_context.fill_style = "rgba(255, 0, 0, 0.10)"
drawing_context.fill()
if self.focused:
stroke_style = focused_style
drawing_context.begin_path()
drawing_context.rect(2, 2, canvas_size.width - 4, canvas_size.height - 4)
drawing_context.line_join = "miter"
drawing_context.stroke_style = stroke_style
drawing_context.line_width = 4.0
drawing_context.stroke()

def drag_enter(self, mime_data: UserInterface.MimeData) -> str:
self.__overlay_canvas_item.is_dropping = True
self.__dropping = True
self.update()
return "ignore"

def drag_leave(self) -> str:
self.__overlay_canvas_item.is_dropping = False
self.__dropping = False
self.update()
return "ignore"

def drop(self, mime_data: UserInterface.MimeData, x: int, y: int) -> str:
Expand Down Expand Up @@ -232,16 +174,15 @@ def __init__(self, ui: UserInterface.UserInterface, thumbnail_source: AbstractTh
self.on_delete: typing.Optional[typing.Callable[[], None]] = None

# set up the initial bitmap and overlay canvas items.
bitmap_overlay_canvas_item = BitmapOverlayCanvasItemComposition()
bitmap_overlay_canvas_item = BitmapOverlayCanvasItem()
bitmap_canvas_item = CanvasItem.BitmapCanvasItem(background_color="#CCC", border_color="#444")
bitmap_overlay_canvas_item.insert_canvas_item(bitmap_overlay_canvas_item.canvas_items_count - 1, bitmap_canvas_item)
bitmap_overlay_canvas_item.add_canvas_item(bitmap_canvas_item)
if size is not None:
bitmap_canvas_item.update_sizing(bitmap_canvas_item.sizing.with_fixed_size(size))
bitmap_overlay_canvas_item.update_sizing(bitmap_overlay_canvas_item.sizing.with_fixed_size(size))
for overlay_canvas_item in thumbnail_source.overlay_canvas_items:
overlay_canvas_item.update_sizing(overlay_canvas_item.sizing.with_fixed_size(size))
for overlay_canvas_item in thumbnail_source.overlay_canvas_items:
bitmap_overlay_canvas_item.insert_canvas_item(bitmap_overlay_canvas_item.canvas_items_count - 1, overlay_canvas_item)
bitmap_overlay_canvas_item.add_canvas_item(overlay_canvas_item)

# handle overlay drop callback by forwarding to the callback set by the caller.
def drop_mime_data(mime_data: UserInterface.MimeData, x: int, y: int) -> str:
Expand Down Expand Up @@ -295,10 +236,9 @@ def set_thumbnail_source(self, thumbnail_source: AbstractThumbnailSource) -> Non
if self.__thumbnail_size is not None:
for overlay_canvas_item in thumbnail_source.overlay_canvas_items:
overlay_canvas_item.update_sizing(overlay_canvas_item.sizing.with_fixed_size(self.__thumbnail_size))
while self.__bitmap_overlay_canvas_item.canvas_items_count > 1:
self.__bitmap_overlay_canvas_item.remove_canvas_item(self.__bitmap_overlay_canvas_item.canvas_items[0])
self.__bitmap_overlay_canvas_item.remove_all_canvas_items()
for overlay_canvas_item in thumbnail_source.overlay_canvas_items:
self.__bitmap_overlay_canvas_item.insert_canvas_item(self.__bitmap_overlay_canvas_item.canvas_items_count - 1, overlay_canvas_item)
self.__bitmap_overlay_canvas_item.add_canvas_item(overlay_canvas_item)
self.__thumbnail_source.on_thumbnail_data_changed = ReferenceCounting.weak_partial(ThumbnailCanvasItem.__thumbnail_data_changed, self)
self.__thumbnail_data_changed(self.__thumbnail_source.thumbnail_data)

Expand Down Expand Up @@ -370,23 +310,6 @@ def close(self) -> None:
super().close()


class IsLiveOverlayCanvasItemComposer(CanvasItem.BaseComposer):
def __init__(self, canvas_item: CanvasItem.AbstractCanvasItem, layout_sizing: CanvasItem.Sizing, cache: CanvasItem.ComposerCache, is_active: bool) -> None:
super().__init__(canvas_item, layout_sizing, cache)
self.__is_active = is_active

def _repaint(self, drawing_context: DrawingContext.DrawingContext, canvas_bounds: Geometry.IntRect, composer_cache: CanvasItem.ComposerCache) -> None:
is_active = self.__is_active
with drawing_context.saver():
drawing_context.translate(canvas_bounds.left, canvas_bounds.top)
if is_active:
with drawing_context.saver():
drawing_context.begin_path()
drawing_context.round_rect(2, 2, 6, 6, 3)
drawing_context.fill_style = "rgba(0, 255, 0, 0.80)"
drawing_context.fill()


class IsLiveOverlayCanvasItem(CanvasItem.AbstractCanvasItem):
def __init__(self) -> None:
super().__init__()
Expand All @@ -402,8 +325,14 @@ def active(self, value: bool) -> None:
self.__active = value
self.update()

def _get_composer(self, composer_cache: CanvasItem.ComposerCache) -> typing.Optional[CanvasItem.BaseComposer]:
return IsLiveOverlayCanvasItemComposer(self, self.layout_sizing, composer_cache, self.__active)
def _repaint(self, drawing_context: DrawingContext.DrawingContext) -> None:
super()._repaint(drawing_context)
if self.active:
with drawing_context.saver():
drawing_context.begin_path()
drawing_context.round_rect(2, 2, 6, 6, 3)
drawing_context.fill_style = "rgba(0, 255, 0, 0.80)"
drawing_context.fill()


class DataItemThumbnailSource(AbstractThumbnailSource):
Expand Down
24 changes: 14 additions & 10 deletions nion/swift/DataPanel.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,10 +249,12 @@ def audited_func() -> None:


class ItemExplorerController:
def __init__(self, ui: UserInterface.UserInterface,
def __init__(self, event_loop: asyncio.AbstractEventLoop, ui: UserInterface.UserInterface,
canvas_item: CanvasItem.AbstractCanvasItem,
display_item_adapters_model: ListModel.MappedListModel, selection: Selection.IndexedSelection,
direction: GridCanvasItem.Direction = GridCanvasItem.Direction.Row, wrap: bool = True) -> None:
self.__event_loop = event_loop
self.__thread_helper = ThreadHelper(event_loop)
self.__pending_tasks: typing.List[asyncio.Task[None]] = list()
self.ui = ui
self.__selection = selection
Expand Down Expand Up @@ -319,6 +321,8 @@ def selection_changed() -> None:

def close(self) -> None:
assert not self.__closed
self.__thread_helper.close()
self.__thread_helper = typing.cast(typing.Any, None)
for pending_task in self.__pending_tasks:
pending_task.cancel()
self.__pending_tasks = typing.cast(typing.Any, None)
Expand Down Expand Up @@ -421,7 +425,7 @@ def _test_get_display_item_adapter(self, index: int) -> DisplayItemAdapter:
return self.__display_item_adapters[index]

def __display_item_adapter_needs_update(self) -> None:
self.__list_canvas_item.update()
self.__thread_helper.call_on_main_thread("list_canvas_item.update", self.__list_canvas_item.update)

# call this method to insert a display item
# not thread safe
Expand Down Expand Up @@ -522,18 +526,18 @@ def drag_started(self, index: int, x: int, y: int, modifiers: UserInterface.Keyb


class DataListController(ItemExplorerController):
def __init__(self, ui: UserInterface.UserInterface, display_item_adapters_model: ListModel.MappedListModel,
selection: Selection.IndexedSelection) -> None:
def __init__(self, event_loop: asyncio.AbstractEventLoop, ui: UserInterface.UserInterface,
display_item_adapters_model: ListModel.MappedListModel, selection: Selection.IndexedSelection) -> None:
canvas_item = ListCanvasItem.ListCanvasItem(ListCanvasItemDelegate(self), selection)
super().__init__(ui, canvas_item, display_item_adapters_model, selection, GridCanvasItem.Direction.Row, True)
super().__init__(event_loop, ui, canvas_item, display_item_adapters_model, selection, GridCanvasItem.Direction.Row, True)


class DataGridController(ItemExplorerController):
def __init__(self, ui: UserInterface.UserInterface, display_item_adapters_model: ListModel.MappedListModel,
selection: Selection.IndexedSelection,
def __init__(self, event_loop: asyncio.AbstractEventLoop, ui: UserInterface.UserInterface,
display_item_adapters_model: ListModel.MappedListModel, selection: Selection.IndexedSelection,
direction: GridCanvasItem.Direction = GridCanvasItem.Direction.Row, wrap: bool = True) -> None:
canvas_item = GridCanvasItem.GridCanvasItem(GridCanvasItemDelegate(self), selection, direction, wrap)
super().__init__(ui, canvas_item, display_item_adapters_model, selection, direction, wrap)
super().__init__(event_loop, ui, canvas_item, display_item_adapters_model, selection, direction, wrap)


class ItemExplorerWidget(Widgets.CompositeWidgetBase):
Expand Down Expand Up @@ -601,12 +605,12 @@ def focus_changed(focused: bool) -> None:
def delete_display_item_adapters(display_item_adapters: typing.List[DisplayItemAdapter]) -> None:
document_controller.delete_display_items([display_item_adapter.display_item for display_item_adapter in display_item_adapters if display_item_adapter.display_item])

self.data_list_controller = DataListController(ui, self.__filtered_display_item_adapters_model, self.__selection)
self.data_list_controller = DataListController(document_controller.event_loop, ui, self.__filtered_display_item_adapters_model, self.__selection)
self.data_list_controller.on_context_menu_event = show_context_menu
self.data_list_controller.on_focus_changed = focus_changed
self.data_list_controller.on_delete_display_item_adapters = delete_display_item_adapters

self.data_grid_controller = DataGridController(ui, self.__filtered_display_item_adapters_model, self.__selection)
self.data_grid_controller = DataGridController(document_controller.event_loop, ui, self.__filtered_display_item_adapters_model, self.__selection)
self.data_grid_controller.on_context_menu_event = show_context_menu
self.data_grid_controller.on_focus_changed = focus_changed
self.data_grid_controller.on_delete_display_item_adapters = delete_display_item_adapters
Expand Down
Loading

0 comments on commit 41d6dec

Please sign in to comment.