Skip to content

Commit f1b739e

Browse files
committed
show save button if there are regions presents
1 parent ee5f676 commit f1b739e

File tree

6 files changed

+93
-53
lines changed

6 files changed

+93
-53
lines changed

client/src/Annotator/index.jsx

+44-10
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import PropTypes from "prop-types"
1515
import noopReducer from "./reducers/noop-reducer.js"
1616
import {useTranslation} from "react-i18next"
1717
import getActiveImage from "./reducers/get-active-image.js"
18-
import { saveActiveImage } from "../utils/send-data-to-server"
18+
import { saveActiveImage, saveData, splitRegionData, getImageData} from "../utils/send-data-to-server"
1919
import { useSnackbar} from "../SnackbarContext/index.jsx"
2020
export const Annotator = ({
2121
images,
@@ -124,21 +124,55 @@ export const Annotator = ({
124124
showSnackbar(error.message, 'error');
125125
});
126126
}
127-
128-
const dispatch = useEventCallback((action) => {
127+
const preprocessDataBeforeSend = async (output) => {
128+
const selectedImageIndex = output.selectedImage;
129+
let _image = output.images[selectedImageIndex];
130+
let regions = _image['regions'] || [];
131+
let imageData = getImageData(_image);
132+
133+
imageData['regions'] = [];
134+
for (let regionNum = 0; regionNum < regions.length; regionNum++) {
135+
imageData['regions'].push(splitRegionData(regions[regionNum]));
136+
}
137+
138+
try {
139+
const response = await saveData(imageData);
140+
showSnackbar(response.message, 'success');
141+
return imageData['regions'];
142+
} catch (error) {
143+
showSnackbar(error.message, 'error');
144+
return [];
145+
}
146+
};
147+
148+
const dispatch = useEventCallback(async (action) => {
129149
if (action.type === "HEADER_BUTTON_CLICKED") {
130150
if (["Exit", "Done", "Save", "Complete"].includes(action.buttonName)) {
131-
return onExit(without(state, "history"))
151+
// save the current data
152+
if (action.buttonName === "Save") {
153+
const result = await preprocessDataBeforeSend(without(state, "history"));
154+
dispatchToReducer({
155+
type: "SAVE_LAST_REGIONS",
156+
payload: result
157+
});
158+
dispatchToReducer({
159+
type: "ENABLE_DOWNLOAD",
160+
payload: result
161+
});
162+
return null;
163+
} else {
164+
return onExit(without(state, "history"));
165+
}
132166
} else if (action.buttonName === "Next" && onNextImage) {
133-
saveCurrentData(getActiveImage(state).activeImage)
134-
return onNextImage(without(state, "history"))
167+
saveCurrentData(getActiveImage(state).activeImage);
168+
return onNextImage(without(state, "history"));
135169
} else if (action.buttonName === "Prev" && onPrevImage) {
136-
saveCurrentData(getActiveImage(state).activeImage)
137-
return onPrevImage(without(state, "history"))
170+
saveCurrentData(getActiveImage(state).activeImage);
171+
return onPrevImage(without(state, "history"));
138172
}
139173
}
140-
dispatchToReducer(action)
141-
})
174+
dispatchToReducer(action);
175+
});
142176

143177
const onRegionClassAdded = useEventCallback((cls) => {
144178
dispatchToReducer({

client/src/DemoSite/index.jsx

+36-34
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,50 @@
11
import Annotator from "../Annotator"
22
import React, { useEffect, useState } from "react"
3-
import {saveData, splitRegionData, getImageData} from '../utils/send-data-to-server'
43
import SetupPage from "../SetupPage";
5-
import { useSnackbar } from '../SnackbarContext';
64
import { useSettings } from "../SettingsProvider";
5+
import {setIn} from "seamless-immutable"
6+
7+
const extractRelevantProps = (region) => ({
8+
cls: region.cls,
9+
comment: region.comment,
10+
id: region.id,
11+
});
712

813
const userReducer = (state, action) => {
914
switch (action.type) {
10-
// case "SELECT_CLASSIFICATION": {
11-
// switch (action.cls) {
12-
// case "One": {
13-
// return setIn(state, ["selectedTool"], "create-box");
14-
// }
15-
// case "Two": {
16-
// return setIn(state, ["selectedTool"], "create-polygon");
17-
// }
18-
// }
19-
// }
15+
case "CLOSE_REGION_EDITOR":
16+
case "DELETE_REGION": {
17+
const { images, selectedImage } = state;
18+
const lastRegions = state.lastRegions || [];
19+
if (selectedImage != null && lastRegions) {
20+
const currentImage = images[selectedImage];
21+
const regions = currentImage ? (currentImage.regions || []) : [];
22+
if (
23+
regions.length !== lastRegions.length ||
24+
!regions.every((region, index) => {
25+
const lastRegion = lastRegions[index] || [];
26+
const currentProps = extractRelevantProps(region);
27+
const lastProps = extractRelevantProps(lastRegion);
28+
return JSON.stringify(currentProps) === JSON.stringify(lastProps);
29+
})
30+
) {
31+
return setIn(state, ["hasNewChange"], true);
32+
} else {
33+
return setIn(state, ["hasNewChange"], false);
34+
}
35+
}
36+
}
37+
case "SAVE_LAST_REGIONS": {
38+
return setIn(state, ["lastRegions"], action.payload);
39+
}
40+
case "ENABLE_DOWNLOAD": {
41+
return setIn(state, ["enabledDownload"], true);
42+
}
2043
}
21-
2244
return state;
2345
};
2446

25-
26-
2747
export default () => {
28-
const { showSnackbar } = useSnackbar();
2948
const [selectedImageIndex, changeSelectedImageIndex] = useState(0)
3049
const [showLabel, setShowLabel] = useState(false)
3150
const [imageNames, setImageNames] = useState([])
@@ -42,23 +61,6 @@ export default () => {
4261
}
4362
})
4463

45-
const preprocessDataBeforeSend = (output) => {
46-
const selectedImageIndex = output.selectedImage;
47-
let _image = output.images[selectedImageIndex]
48-
let regions = _image['regions'] || []
49-
let imageData = getImageData(_image)
50-
51-
imageData['regions'] = []
52-
for (let regionNum = 0; regionNum < regions.length; regionNum++){
53-
imageData['regions'].push(splitRegionData(regions[regionNum]))
54-
}
55-
saveData(imageData).then(response => {
56-
showSnackbar(response.message, 'success');
57-
})
58-
.catch(error => {
59-
showSnackbar(error.message, 'error');
60-
});
61-
}
6264

6365
const [loading, setLoading] = useState(true); // Add loading state
6466
const onSelectJumpHandle = (selectedImageName) => {
@@ -144,7 +146,7 @@ export default () => {
144146
enabledRegionProps= {["class", "comment"]}
145147
userReducer= {userReducer}
146148
onExit={(output) => {
147-
preprocessDataBeforeSend(output)
149+
console.log("Exiting!")
148150
}}
149151
settings={settings}
150152
onSelectJump={onSelectJumpHandle}

client/src/MainLayout/index.jsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -224,17 +224,18 @@ export const MainLayout = ({
224224
) : null,
225225
].filter(Boolean)}
226226
headerItems={[
227+
{ name: "Download", label:t("btn.download"), disabled: !state.enabledDownload, hasInnerMenu: true},
227228
!hidePrev && {name: "Prev", label: t("btn.previous"), disabled: disabledNextAndPrev},
228229
!hideNext && {name: "Next", label: t("btn.next"), disabled: disabledNextAndPrev},
229230
state.annotationType !== "video"
230231
? null
231232
: !state.videoPlaying
232233
? {name: "Play", label: t("btn.play")}
233234
: {name: "Pause", label: t("btn.pause")},
234-
!hideClone &&
235+
!hideClone && state.hasNewChange &&
235236
!nextImageHasRegions &&
236237
activeImage.regions && {name: "Clone", label: t("btn.clone")},
237-
!hideSave && {name: "Save", label:t("btn.save"), icon: <Save />},
238+
!hideSave && {name: "Save", label:t("btn.save"), disabled: !state.hasNewChange, icon: <Save />},
238239
!hideSettings && {name: "Settings", label: t("btn.settings")},
239240
{name: "Exit", label:t("btn.exit"), icon: <ExitToApp />}
240241
].filter(Boolean)}

client/src/SetupPage/index.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ export const SetupPage = ({setConfiguration, settings, setShowLabel}) => {
125125
{settings.taskChoice === "image_classification" && (
126126
<>
127127
<ConfigureImageClassification config={settings.configuration} onChange={updateConfiguration} />
128-
<Box display="flex" justifyContent="end">
128+
<Box display="flex" justifyContent="end" paddingBottom="6rem">
129129
<Button variant="contained" disabled={!hasConfig} onClick={() => setTab("image")} disableElevation>
130130
{t("btn.next")}
131131
</Button>
@@ -136,7 +136,7 @@ export const SetupPage = ({setConfiguration, settings, setShowLabel}) => {
136136
{settings.taskChoice === "image_segmentation" && (
137137
<>
138138
<ConfigureImageSegmentation config={settings.configuration} onChange={updateConfiguration} />
139-
<Box display="flex" justifyContent="end">
139+
<Box display="flex" justifyContent="end" paddingBottom="6rem">
140140
<Button variant="contained" disabled={!hasConfig} onClick={() => setTab("image")} disableElevation>
141141
{t("btn.next")}
142142
</Button>

client/src/workspace/DownloadButton/index.jsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { hexToRgbTuple } from "../../utils/color-utils.js";
1212
import HeaderButton from "../HeaderButton/index.jsx";
1313
import { useTranslation } from "react-i18next"
1414

15-
const DownloadButton = ({selectedImageName, classList, hideHeaderText}) => {
15+
const DownloadButton = ({selectedImageName, classList, hideHeaderText, disabled}) => {
1616
const [anchorEl, setAnchorEl] = useState(null);
1717
const { showSnackbar } = useSnackbar();
1818
const {t} = useTranslation();
@@ -80,6 +80,7 @@ const DownloadButton = ({selectedImageName, classList, hideHeaderText}) => {
8080
name={"Download"}
8181
label={t("btn.download")}
8282
onClick={handleClick}
83+
disabled={disabled}
8384
icon={<DownloadIcon />}
8485
/>
8586
<Menu

client/src/workspace/Header/index.jsx

+6-4
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,20 @@ export const Header = ({
3333
}) => {
3434

3535
const{ t } = useTranslation()
36-
36+
const downloadMenu = items.find((item) => item.name === "Download")
37+
3738
return (
3839
<ThemeProvider theme={theme}>
3940
<Container>
4041
<BrandText flexGrow={1}>
4142
{t("labname")}
4243
</BrandText>
4344
<Box flexGrow={1}>{leftSideContent}</Box>
44-
<DownloadButton selectedImageName={selectedImageName} classList={classList} hideHeaderText={hideHeaderText}
45-
onDownload={onClickItem}
45+
{downloadMenu && <DownloadButton selectedImageName={selectedImageName} classList={classList} hideHeaderText={hideHeaderText}
46+
onDownload={onClickItem} disabled={downloadMenu.disabled}
4647
/>
47-
{items.map((item) => (
48+
}
49+
{items.filter(item => item.name !== "Download").map((item) => (
4850
<HeaderButton
4951
key={item.name}
5052
hideText={hideHeaderText}

0 commit comments

Comments
 (0)