adding canvas tools(flet ui)

This commit is contained in:
aedhcarrick 2022-12-22 14:17:29 -06:00
parent b110e31bd2
commit 784840c50e
4 changed files with 158 additions and 34 deletions

View File

@ -48,10 +48,10 @@ class AssetManager(ft.Container):
tab.content.margin = margin tab.content.margin = margin
class AssetPanel(ft.Container): class AssetPanel(ft.Container):
pass pass
class LayerPanel(ft.Container): class LayerPanel(ft.Container):
def update_layers(self): def update_layers(self):
self.layers = self.content.content.controls self.layers = self.content.content.controls
@ -152,9 +152,11 @@ class LayerPanel(ft.Container):
self.layers.insert(index, layer) self.layers.insert(index, layer)
self.update_layers() self.update_layers()
class LayerSlot(ft.Container): class LayerSlot(ft.Container):
pass pass
def layer_left_click(e: ft.TapEvent): def layer_left_click(e: ft.TapEvent):
index = layer_panel.get_layer_index_from_position(e.local_y) index = layer_panel.get_layer_index_from_position(e.local_y)
if index >= len(layer_panel.layers): if index >= len(layer_panel.layers):
@ -181,6 +183,8 @@ def on_layer_drag(e: ft.DragUpdateEvent):
def drop_layer(e: ft.DragEndEvent): def drop_layer(e: ft.DragEndEvent):
layer_panel.layer_being_moved = None layer_panel.layer_being_moved = None
# LayerPanel == ft.Container
layer_panel = LayerPanel( layer_panel = LayerPanel(
content = ft.GestureDetector( content = ft.GestureDetector(
content = ft.Column( content = ft.Column(
@ -203,6 +207,8 @@ layer_panel.visible_layers = []
layer_panel.layer_being_moved = None layer_panel.layer_being_moved = None
layer_panel.layer_last_index = 0 layer_panel.layer_last_index = 0
# AssetPanel == ft.Container
asset_panel = AssetPanel( asset_panel = AssetPanel(
content = ft.Column( content = ft.Column(
controls = [ controls = [
@ -223,6 +229,8 @@ asset_manager_dragbar = ft.GestureDetector(
content = ft.VerticalDivider(), content = ft.VerticalDivider(),
) )
# AssetManager == ft.Container
asset_manager = AssetManager( asset_manager = AssetManager(
content = ft.Row( content = ft.Row(
controls = [ controls = [
@ -258,9 +266,9 @@ asset_manager = AssetManager(
) )
asset_manager.tabs = asset_manager.content.controls[0].controls[0].tabs asset_manager.tabs = asset_manager.content.controls[0].controls[0].tabs
asset_manager.dragbar = asset_manager_dragbar
asset_manager.layer_panel = layer_panel asset_manager.layer_panel = layer_panel
asset_manager.asset_panel = asset_panel asset_manager.asset_panel = asset_panel
asset_manager.dragbar = asset_manager_dragbar
''' '''
# keep track of which layers are visible # keep track of which layers are visible

View File

@ -32,6 +32,7 @@ class Canvas(ft.Container):
def refresh_canvas(self): def refresh_canvas(self):
self.overlay.refresh_canvas_overlay() self.overlay.refresh_canvas_overlay()
self.page.update()
def set_current_tool(self, tool): def set_current_tool(self, tool):
self.page.current_tool = tool self.page.current_tool = tool
@ -92,8 +93,25 @@ class Canvas(ft.Container):
self.image_stack.get_scaled_size() self.image_stack.get_scaled_size()
self.align_canvas(e) self.align_canvas(e)
def pan_canvas(e): def set_current_tool(self, tool):
canvas.pan_canvas(e) if tool == 'pan':
self.overlay.controls.pop(2)
self.overlay.controls.insert(2,pan_tool)
elif tool == 'move':
self.overlay.controls.pop(2)
self.overlay.controls.insert(2,move_tool)
elif tool == 'box_select':
self.overlay.controls.pop(2)
self.overlay.controls.insert(2,box_select_tool)
elif tool == 'brush':
self.overlay.controls.pop(2)
self.overlay.controls.insert(2,brush_tool)
elif tool == 'fill':
self.overlay.controls.pop(2)
self.overlay.controls.insert(2,fill_tool)
else:
pass
self.update()
class ImageStack(ft.Container): class ImageStack(ft.Container):
def add_canvas_background(self): def add_canvas_background(self):
@ -112,6 +130,8 @@ class ImageStack(ft.Container):
), ),
) )
canvas_bg.image = image canvas_bg.image = image
canvas_bg.offset_x = 0
canvas_bg.offset_y = 0
self.canvas_bg = canvas_bg self.canvas_bg = canvas_bg
self.content.controls.append(canvas_bg) self.content.controls.append(canvas_bg)
@ -144,14 +164,17 @@ class ImageStack(ft.Container):
), ),
) )
layer_image.image = image layer_image.image = image
layer_image.offset_x = 0
layer_image.offset_y = 0
self.center_layer(layer_image) self.center_layer(layer_image)
self.content.controls.append(layer_image) self.content.controls.append(layer_image)
self.page.tool_manager.enable_tools()
canvas.refresh_canvas() canvas.refresh_canvas()
def get_preview(self): def get_preview(self):
stack = [] stack = []
for layer in self.content.controls: for layer in self.content.controls:
stack.append(layer.image) stack.append(layer)
return flet_utils.get_preview_from_stack(self.page.canvas_size, stack) return flet_utils.get_preview_from_stack(self.page.canvas_size, stack)
def get_scaled_size(self): def get_scaled_size(self):
@ -159,28 +182,44 @@ class ImageStack(ft.Container):
self.scaled_height = self.height * self.scale self.scaled_height = self.height * self.scale
def center_layer(self, layer_image): def center_layer(self, layer_image):
layer_image.offset_x = 0
layer_image.offset_y = 0
layer_image.left = (self.width * 0.5) - (layer_image.width * 0.5) layer_image.left = (self.width * 0.5) - (layer_image.width * 0.5)
layer_image.top = (self.height * 0.5) - (layer_image.height * 0.5) layer_image.top = (self.height * 0.5) - (layer_image.height * 0.5)
def align_layer(self, layer_image): def align_layer(self, layer_image):
layer_image.left = (self.width * 0.5) - (layer_image.width * 0.5) layer_image.left = ((self.width - layer_image.width) * 0.5) + layer_image.offset_x
layer_image.top = (self.height * 0.5) - (layer_image.height * 0.5) layer_image.top = ((self.height - layer_image.height) * 0.5) + layer_image.offset_y
def drag_layer(self, e): def move_layer(self, e: ft.DragUpdateEvent):
index = self.page.active_layer.index
layer = self.content.controls[index+1]
layer.offset_x += e.delta_x
layer.offset_y += e.delta_y
self.align_layer(layer)
self.update()
def finish_move_layer(self, e: ft.DragEndEvent):
canvas.refresh_canvas()
def resize_layer(self, e: ft.DragUpdateEvent):
pass pass
def resize_layer(self, e): def box_select(self, e):
pass pass
def draw_on_layer(self, e): def bucket_fill(self, e):
pass pass
def paint_on_layer(self, e):
pass
class LayerImage(ft.Container): class LayerImage(ft.Container):
pass pass
class CanvasGestures(ft.GestureDetector):
pass
class CanvasOverlay(ft.Stack): class CanvasOverlay(ft.Stack):
def refresh_canvas_overlay(self): def refresh_canvas_overlay(self):
self.refresh_canvas_size_display() self.refresh_canvas_size_display()
@ -199,9 +238,7 @@ class CanvasOverlay(ft.Stack):
self.page.property_manager.set_preview_image(preview) self.page.property_manager.set_preview_image(preview)
def pan_canvas(e): # ImageStack == ft.Container
canvas.pan_canvas(e)
image_stack = ImageStack( image_stack = ImageStack(
width = 4096, width = 4096,
height = 4096, height = 4096,
@ -224,6 +261,8 @@ canvas_cover = ft.Container(
opacity = 0.5, opacity = 0.5,
) )
# LayerImage == ft.Container
canvas_preview = LayerImage( canvas_preview = LayerImage(
width = 4096, width = 4096,
height = 4096, height = 4096,
@ -238,12 +277,68 @@ canvas_preview = LayerImage(
) )
canvas_gestures = ft.GestureDetector( # CanvasGestures == ft.GestureDetector
mouse_cursor = ft.MouseCursor.MOVE, def pan_canvas(e):
canvas.pan_canvas(e)
pan_tool = CanvasGestures(
mouse_cursor = ft.MouseCursor.GRAB,
drag_interval = 10, drag_interval = 10,
on_pan_update = pan_canvas, on_pan_update = pan_canvas,
) )
def select_layer(e):
pass
def move_layer(e):
image_stack.move_layer(e)
def finish_move_layer(e):
image_stack.finish_move_layer(e)
move_tool = CanvasGestures(
mouse_cursor = ft.MouseCursor.MOVE,
drag_interval = 10,
on_pan_start = select_layer,
on_pan_update = move_layer,
on_pan_end = finish_move_layer,
)
def set_select_start(e):
pass
def draw_select_box(e):
pass
def get_box_select(e):
pass
box_select_tool = CanvasGestures(
mouse_cursor = ft.MouseCursor.GRAB,
drag_interval = 10,
on_pan_start = set_select_start,
on_pan_update = draw_select_box,
on_pan_end = get_box_select,
)
def draw_on_layer(e):
pass
brush_tool = CanvasGestures(
mouse_cursor = ft.MouseCursor.GRAB,
drag_interval = 10,
on_pan_update = draw_on_layer,
)
def fill_selection(e):
pass
fill_tool = CanvasGestures(
mouse_cursor = ft.MouseCursor.GRAB,
drag_interval = 10,
on_tap = fill_selection,
)
canvas_size_display = ft.Container( canvas_size_display = ft.Container(
content = ft.Text( content = ft.Text(
value = "test", value = "test",
@ -305,11 +400,13 @@ canvas_tools.center = center_canvas_button
canvas_tools.zoom_in = zoom_in_button canvas_tools.zoom_in = zoom_in_button
canvas_tools.zoom_out = zoom_out_button canvas_tools.zoom_out = zoom_out_button
# CanvasOverlay == ft.Stack
canvas_overlay = CanvasOverlay( canvas_overlay = CanvasOverlay(
[ [
canvas_cover, canvas_cover,
canvas_preview, canvas_preview,
canvas_gestures, pan_tool,
canvas_size_display, canvas_size_display,
canvas_tools, canvas_tools,
], ],
@ -321,7 +418,7 @@ canvas_overlay.size_display = canvas_size_display
canvas_overlay.tools = canvas_tools canvas_overlay.tools = canvas_tools
# Canvas = ft.Container
canvas = Canvas( canvas = Canvas(
content = ft.Stack( content = ft.Stack(
[ [

View File

@ -7,19 +7,20 @@ import flet as ft
from scripts import flet_utils from scripts import flet_utils
def open_gallery(e): def open_gallery(e):
e.control.page.open_gallery(e) e.page.open_gallery(e)
def blank_layer(e): def blank_layer(e):
e.control.page.asset_manager.add_blank_layer(e) e.page.asset_manager.add_blank_layer(e)
def load_image(e): def load_image(e):
e.control.page.file_picker.pick_files(file_type = 'image', allow_multiple = True) e.page.file_picker.pick_files(file_type = 'image', allow_multiple = True)
def tool_select(e): def tool_select(e):
toolbox.clear_tools() toolbox.clear_tools()
e.control.page.current_tool = e.control.data['label'] e.page.current_tool = e.control.data['label']
e.page.canvas.set_current_tool(e.control.data['label'])
e.control.selected = True e.control.selected = True
e.control.page.update() e.page.update()
class Action(): class Action():
@ -28,6 +29,8 @@ class Action():
self.icon = icon self.icon = icon
self.tooltip = tooltip self.tooltip = tooltip
self.on_click = on_click self.on_click = on_click
self.disabled = False
action_list = [ action_list = [
Action('gallery', ft.icons.DASHBOARD_OUTLINED, 'Gallery', open_gallery), Action('gallery', ft.icons.DASHBOARD_OUTLINED, 'Gallery', open_gallery),
@ -35,20 +38,24 @@ action_list = [
Action('load image', ft.icons.IMAGE_OUTLINED, 'Load image as layer', load_image), Action('load image', ft.icons.IMAGE_OUTLINED, 'Load image as layer', load_image),
] ]
class Tool(): class Tool():
def __init__(self, label, icon, tooltip, on_click): def __init__(self, label, icon, tooltip):
self.label = label self.label = label
self.icon = icon self.icon = icon
self.tooltip = tooltip self.tooltip = tooltip
self.on_click = on_click self.on_click = tool_select
self.disabled = True
tool_list = [ tool_list = [
Tool('move', ft.icons.OPEN_WITH_OUTLINED, 'Move layer(s)', tool_select), Tool('move', ft.icons.OPEN_WITH_OUTLINED, 'Move layer(s)'),
Tool('select', ft.icons.HIGHLIGHT_ALT_OUTLINED, 'Select tool', tool_select), Tool('select', ft.icons.HIGHLIGHT_ALT_OUTLINED, 'Select tool'),
Tool('brush', ft.icons.BRUSH_OUTLINED, 'Brush tool', tool_select), Tool('brush', ft.icons.BRUSH_OUTLINED, 'Brush tool'),
Tool('fill', ft.icons.FORMAT_COLOR_FILL_OUTLINED, 'Fill tool', tool_select), Tool('fill', ft.icons.FORMAT_COLOR_FILL_OUTLINED, 'Fill tool'),
] ]
class ToolManager(ft.Container): class ToolManager(ft.Container):
def setup(self): def setup(self):
self.toolbox.get_tools() self.toolbox.get_tools()
@ -82,6 +89,11 @@ class ToolManager(ft.Container):
toolbox.height = self.page.toolbox_height toolbox.height = self.page.toolbox_height
self.update() self.update()
def enable_tools(self):
for tool in self.toolbox.content.controls:
tool.disabled = False
self.update()
class ToolBox(ft.Container): class ToolBox(ft.Container):
def get_tools(self): def get_tools(self):
for action in action_list: for action in action_list:
@ -109,6 +121,7 @@ class ToolBox(ft.Container):
tooltip = button_info.tooltip, tooltip = button_info.tooltip,
data = {'label':button_info.label}, data = {'label':button_info.label},
on_click = button_info.on_click, on_click = button_info.on_click,
disabled = button_info.disabled,
) )
return button return button
@ -120,6 +133,7 @@ class ToolBox(ft.Container):
class ToolPropertyPanel(ft.Container): class ToolPropertyPanel(ft.Container):
pass pass
# ToolBox == ft.Container # ToolBox == ft.Container
toolbox = ToolBox( toolbox = ToolBox(
clip_behavior = 'antiAlias', clip_behavior = 'antiAlias',
@ -148,6 +162,7 @@ tool_divider = ft.GestureDetector(
), ),
) )
# ToolPropertyPanel == ft.Container # ToolPropertyPanel == ft.Container
tool_properties = ToolPropertyPanel( tool_properties = ToolPropertyPanel(
content = ft.Column( content = ft.Column(
@ -165,6 +180,7 @@ tool_manager_dragbar = ft.GestureDetector(
content = ft.VerticalDivider(), content = ft.VerticalDivider(),
) )
# ToolManager = ft.Container # ToolManager = ft.Container
tool_manager = ToolManager( tool_manager = ToolManager(
content = ft.Row( content = ft.Row(

View File

@ -99,14 +99,17 @@ def get_canvas_background(path):
# takes list of Image(s) as arg # takes list of Image(s) as arg
# returns single composite of all images # returns single composite of all images
def get_preview_from_stack(size, images): def get_preview_from_stack(size, stack):
preview = create_blank_image(size) preview = create_blank_image(size)
canvas_width = size[0] canvas_width = size[0]
canvas_height = size[1] canvas_height = size[1]
for image in images: for layer in stack:
image = layer.image
# need to crop images for composite # need to crop images for composite
x0, y0 = (image.width - canvas_width) * 0.5, (image.height - canvas_height) * 0.5 x0 = ((image.width - canvas_width) * 0.5) - layer.offset_x
x1, y1 = x0 + canvas_width, y0 + canvas_height y0 = ((image.height - canvas_height) * 0.5) - layer.offset_y
x1 = x0 + canvas_width
y1 = y0 + canvas_height
box = (x0, y0, x1, y1) box = (x0, y0, x1, y1)
cropped_image = image.crop(box) cropped_image = image.crop(box)
preview = Image.alpha_composite(preview,cropped_image) preview = Image.alpha_composite(preview,cropped_image)