Skip to content

Commit

Permalink
Merge pull request #5135 from voxel51/enhance/operatorbuttonview
Browse files Browse the repository at this point in the history
Add additional functionality to OperatorButtonView
  • Loading branch information
ritch authored Nov 18, 2024
2 parents 9a5eb8e + 5f5a01b commit 76bd458
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import React from "react";
import { MuiIconFont } from "@fiftyone/components";
import { OperatorExecutionButton } from "@fiftyone/operators";
import { OperatorExecutionButton, usePanelEvent } from "@fiftyone/operators";
import { usePanelId } from "@fiftyone/spaces";
import { isNullish } from "@fiftyone/utilities";
import { Box, ButtonProps, Typography } from "@mui/material";
import { getColorByCode, getComponentProps, getDisabledColors } from "../utils";
import { ViewPropsType } from "../utils/types";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import TooltipProvider from "./TooltipProvider";
import { OperatorExecutionOption } from "@fiftyone/operators/src/state";
import {
ExecutionCallback,
ExecutionErrorCallback,
} from "@fiftyone/operators/src/types-internal";
import { OperatorResult } from "@fiftyone/operators/src/operators";

export default function OperatorExecutionButtonView(props: ViewPropsType) {
const { schema, path } = props;
Expand All @@ -21,6 +27,9 @@ export default function OperatorExecutionButtonView(props: ViewPropsType) {
params = {},
title,
disabled = false,
on_error,
on_success,
on_option_selected,
} = view;
const panelId = usePanelId();
const variant = getVariant(props);
Expand All @@ -35,11 +44,56 @@ export default function OperatorExecutionButtonView(props: ViewPropsType) {
<ExpandMoreIcon />
);

const triggerEvent = usePanelEvent();

const handleOnSuccess: ExecutionCallback = (
operatorResult: OperatorResult,
{ ctx }
) => {
if (on_success) {
triggerEvent(panelId, {
operator: on_success,
params: {
result: operatorResult.result,
original_params: ctx.params,
},
});
}
};
const handleOnError: ExecutionErrorCallback = (
result: OperatorResult,
{ ctx }
) => {
if (on_error) {
triggerEvent(panelId, {
operator: on_error,
params: {
error: result.error,
error_message: result.errorMessage,
original_params: ctx.params,
},
});
}
};
const handleOnOptionSelected = (option: OperatorExecutionOption) => {
if (on_option_selected) {
triggerEvent(panelId, {
operator: on_option_selected,
params: {
selected_option: option,
},
});
}
};

return (
<Box {...getComponentProps(props, "container")}>
<TooltipProvider title={title} {...getComponentProps(props, "tooltip")}>
<OperatorExecutionButton
operatorUri={operator}
onSuccess={handleOnSuccess}
onError={handleOnError}
onOptionSelected={handleOnOptionSelected}
executionParams={computedParams}
variant={variant}
disabled={disabled}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const OperatorExecutionButton = ({
operatorUri,
onSuccess,
onError,
onClick,
executionParams,
onOptionSelected,
disabled,
Expand All @@ -30,6 +31,7 @@ export const OperatorExecutionButton = ({
operatorUri: string;
onSuccess?: ExecutionCallback;
onError?: ExecutionErrorCallback;
onClick?: () => void;
executionParams?: object;
onOptionSelected?: (option: OperatorExecutionOption) => void;
disabled?: boolean;
Expand All @@ -38,6 +40,7 @@ export const OperatorExecutionButton = ({
return (
<OperatorExecutionTrigger
operatorUri={operatorUri}
onClick={onClick}
onSuccess={onSuccess}
onError={onError}
executionParams={executionParams}
Expand Down
23 changes: 10 additions & 13 deletions app/packages/operators/src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1012,21 +1012,18 @@ export function useOperatorExecutor(uri, handlers: any = {}) {
setResult(result.result);
setError(result.error);
setIsDelegated(result.delegated);
handlers.onSuccess?.(result, { ctx });
callback?.(result, { ctx });
if (result.error) {
const isAbortError =
result.error.name === "AbortError" ||
result.error instanceof DOMException;
if (!isAbortError) {
notify({
msg: result.errorMessage || `Operation failed: ${uri}`,
variant: "error",
});
console.error("Error executing operator", uri, result.errorMessage);
console.error(result.error);
}
handlers.onError?.(result, { ctx });
notify({
msg: result.errorMessage || `Operation failed: ${uri}`,
variant: "error",
});
console.error("Error executing operator", uri, result.errorMessage);
console.error(result.error);
} else {
handlers.onSuccess?.(result, { ctx });
}
callback?.(result, { ctx });
} catch (e) {
callback?.(new OperatorResult(operator, null, ctx.executor, e, false), {
ctx,
Expand Down
12 changes: 9 additions & 3 deletions app/packages/operators/src/types-internal.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { ExecutionContext, OperatorResult } from "./operators";

export type ExecutionCallbackOptions = {ctx: ExecutionContext};
export type ExecutionCallback = (result: OperatorResult, options: ExecutionCallbackOptions) => void;
export type ExecutionErrorCallback = (error: Error) => void;
export type ExecutionCallbackOptions = { ctx: ExecutionContext };
export type ExecutionCallback = (
result: OperatorResult,
options: ExecutionCallbackOptions
) => void;
export type ExecutionErrorCallback = (
error: OperatorResult,
options: ExecutionCallbackOptions
) => void;

export type OperatorExecutorOptions = {
delegationTarget?: string;
Expand Down
48 changes: 28 additions & 20 deletions fiftyone/operators/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1377,32 +1377,40 @@ class OperatorExecutionButtonView(Button):
import fiftyone.operators.types as types
operatorButtonView = types.OperatorExecutionButtonView(
icon="expand_more",
operator="execute_custom_operator",
params={"key": "value"},
label="Execute",
description="Executes the specified operator",
exec_button = types.OperatorExecutionButtonView(
label="Execute Simple Op",
variant="contained",
operator="@voxel51/panel-examples/simple_op",
on_success=self.on_success,
on_error=self.on_error,
on_option_selected=self.on_select,
params={"msg": "Hello World!"},
)
inputs = types.Object()
inputs.view("operator_btn", operatorButtonView)
inputs.view("operator_btn", view=exec_button)
Args:
icon (str): an icon for the button. Defaults to "expand_more" if not provided.
label (str): a label for the button.
description (str): a description for the button.
title (str): a tooltip title for the button.
operator (str): the name of the operator to execute when the button is clicked.
params (dict): the parameters to pass to the operator.
prompt (str): a prompt for the operation.
disabled (bool): whether the button is disabled.
"""

def __init__(self, **kwargs):
if "operator" not in kwargs or not isinstance(kwargs["operator"], str):
icon: an icon for the button. Defaults to "expand_more" if not provided.
label: a label for the button.
variant: the variant of the button. Can be "contained" or "outlined".
description: a description for the button.
title: a tooltip title for the button.
operator: the URI of the operator to execute when the button is clicked.
on_success: the URI of the operator to execute when the operator execution is successful.
on_error: the URI of the operator to execute when the operator execution fails.
on_option_selected: the URI of the operator to execute when an option is selected.
params: the parameters dict to pass to the operator.
disabled: whether the button is disabled.
"""

def __init__(self, **kwargs):
if "operator" not in kwargs or (
not isinstance(kwargs["operator"], str)
and not callable(kwargs["operator"])
):
raise ValueError(
"The 'operator' parameter of type str is required."
"The 'operator' parameter of type str or callable is required."
)
super().__init__(**kwargs)

Expand Down

0 comments on commit 76bd458

Please sign in to comment.