[React] Added shortcuts & ability to search a label (#1683)

* Fixed remark-linter errors in CONTRIBUTING.md, added python3-tk to the installation guide

* Added labels search

* Added shortcuts to change color and split

* Updated version

* Updated CHANGELOG.md

* Added title

* Added shortcut hint for change color, added ability to change color of group/label using the shortcut

* Updated CONTRIBUTING.md
main
Boris Sekachev 6 years ago committed by GitHub
parent 9320baeeb1
commit 27e43daf84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,9 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [1.1.0-alpha] - Unreleased ## [1.1.0-alpha] - Unreleased
### Added ### Added
- Throttling policy for unauthenticated users (<https://github.com/opencv/cvat/pull/1531>) - Throttling policy for unauthenticated users (<https://github.com/opencv/cvat/pull/1531>)
- Added default label color table for mask export (https://github.com/opencv/cvat/pull/1549) - Added default label color table for mask export (<https://github.com/opencv/cvat/pull/1549>)
- Added environment variables for Redis and Postgres hosts for Kubernetes deployment support (<https://github.com/opencv/cvat/pull/1641>) - Added environment variables for Redis and Postgres hosts for Kubernetes deployment support (<https://github.com/opencv/cvat/pull/1641>)
- Added visual identification for unavailable formats (https://github.com/opencv/cvat/pull/1567) - Added visual identification for unavailable formats (<https://github.com/opencv/cvat/pull/1567>)
- Shortcut to change color of an activated shape in new UI (Enter) (<https://github.com/opencv/cvat/pull/1683>)
- Shortcut to switch split mode (<https://github.com/opencv/cvat/pull/1683>)
- Built-in search for labels when create an object or change a label (<https://github.com/opencv/cvat/pull/1683>)
### Changed ### Changed
- Removed information about e-mail from the basic user information (<https://github.com/opencv/cvat/pull/1627>) - Removed information about e-mail from the basic user information (<https://github.com/opencv/cvat/pull/1627>)
@ -22,15 +25,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- -
### Fixed ### Fixed
- Fixed dataset filter item representation for imageless dataset items (https://github.com/opencv/cvat/pull/1593) - Fixed dataset filter item representation for imageless dataset items (<https://github.com/opencv/cvat/pull/1593>)
- Fixed interpreter crash when trying to import `tensorflow` with no AVX instructions available (https://github.com/opencv/cvat/pull/1567) - Fixed interpreter crash when trying to import `tensorflow` with no AVX instructions available (<https://github.com/opencv/cvat/pull/1567>)
- Kibana wrong working time calculation with new annotation UI use (<https://github.com/opencv/cvat/pull/1654>) - Kibana wrong working time calculation with new annotation UI use (<https://github.com/opencv/cvat/pull/1654>)
- Wrong rexex for account name validation (<https://github.com/opencv/cvat/pull/1667>) - Wrong rexex for account name validation (<https://github.com/opencv/cvat/pull/1667>)
- Wrong description on register view for the username field (<https://github.com/opencv/cvat/pull/1667>) - Wrong description on register view for the username field (<https://github.com/opencv/cvat/pull/1667>)
- Wrong resolution for resizing a shape (<https://github.com/opencv/cvat/pull/1667>) - Wrong resolution for resizing a shape (<https://github.com/opencv/cvat/pull/1667>)
### Security ### Security
- SQL injection in Django `CVE-2020-9402` (https://github.com/opencv/cvat/pull/1657) - SQL injection in Django `CVE-2020-9402` (<https://github.com/opencv/cvat/pull/1657>)
## [1.0.0] - 2020-05-29 ## [1.0.0] - 2020-05-29
### Added ### Added

@ -14,7 +14,7 @@ Next steps should work on clear Ubuntu 18.04.
- Install necessary dependencies: - Install necessary dependencies:
```sh ```sh
$ sudo apt-get update && sudo apt-get --no-install-recommends install -y ffmpeg build-essential curl redis-server python3-dev python3-pip python3-venv libldap2-dev libsasl2-dev $ sudo apt-get update && sudo apt-get --no-install-recommends install -y ffmpeg build-essential curl redis-server python3-dev python3-pip python3-venv python3-tk libldap2-dev libsasl2-dev
``` ```
Also please make sure that you have installed ffmpeg with all necessary libav* libraries and pkg-config package. Also please make sure that you have installed ffmpeg with all necessary libav* libraries and pkg-config package.
```sh ```sh
@ -60,6 +60,7 @@ for development
- Install npm packages for UI and start UI debug server (run the following command from CVAT root directory): - Install npm packages for UI and start UI debug server (run the following command from CVAT root directory):
```sh ```sh
npm install && \
cd cvat-core && npm install && \ cd cvat-core && npm install && \
cd ../cvat-ui && npm install && npm start cd ../cvat-ui && npm install && npm start
``` ```
@ -83,12 +84,13 @@ for development
You have done! Now it is possible to insert breakpoints and debug server and client of the tool. You have done! Now it is possible to insert breakpoints and debug server and client of the tool.
## How to setup additional components in development environment ## Setup additional components in development environment
### Automatic annotation ### Automatic annotation
- Install OpenVINO on your host machine according to instructions from - Install OpenVINO on your host machine according to instructions from
[OpenVINO website](https://docs.openvinotoolkit.org/latest/index.html) [OpenVINO website](https://docs.openvinotoolkit.org/latest/index.html)
- Add some environment variables (copy code below to the end of ``.env/bin/activate`` file):
- Add some environment variables (copy code below to the end of ``.env/bin/activate`` file):
```sh ```sh
source /opt/intel/openvino/bin/setupvars.sh source /opt/intel/openvino/bin/setupvars.sh
@ -161,9 +163,10 @@ litle exception - we prefere 4 spaces for indentation of nested blocks and state
The project uses [a successful Git branching model](https://nvie.com/posts/a-successful-git-branching-model). The project uses [a successful Git branching model](https://nvie.com/posts/a-successful-git-branching-model).
Thus it has a couple of branches. Some of them are described below: Thus it has a couple of branches. Some of them are described below:
- `origin/master` to be the main branch where the source code of - `origin/master` to be the main branch where the source code of
HEAD always reflects a production-ready state. HEAD always reflects a production-ready state
- `origin/develop` to be the main branch where the source code of
- `origin/develop` to be the main branch where the source code of
HEAD always reflects a state with the latest delivered development HEAD always reflects a state with the latest delivered development
changes for the next release. Some would call this the “integration branch”. changes for the next release. Some would call this the “integration branch”.
@ -173,13 +176,13 @@ The issue tracker is the preferred channel for [bug reports](#bugs),
[features requests](#features) and [submitting pull [features requests](#features) and [submitting pull
requests](#pull-requests), but please respect the following restrictions: requests](#pull-requests), but please respect the following restrictions:
* Please **do not** use the issue tracker for personal support requests (use - Please **do not** use the issue tracker for personal support requests (use
[Stack Overflow](http://stackoverflow.com)). [Stack Overflow](http://stackoverflow.com)).
* Please **do not** derail or troll issues. Keep the discussion on topic and - Please **do not** derail or troll issues. Keep the discussion on topic and
respect the opinions of others. respect the opinions of others.
<a name="bugs"></a> <a name="bugs"></a>
## Bug reports ## Bug reports
A bug is a _demonstrable problem_ that is caused by the code in the repository. A bug is a _demonstrable problem_ that is caused by the code in the repository.
@ -187,13 +190,13 @@ Good bug reports are extremely helpful - thank you!
Guidelines for bug reports: Guidelines for bug reports:
1. **Use the GitHub issue search** &mdash; check if the issue has already been 1. **Use the GitHub issue search** &mdash; check if the issue has already been
reported. reported.
2. **Check if the issue has been fixed** &mdash; try to reproduce it using the 1. **Check if the issue has been fixed** &mdash; try to reproduce it using the
latest `develop` branch in the repository. latest `develop` branch in the repository.
3. **Isolate the problem** &mdash; ideally create a reduced test case. 1. **Isolate the problem** &mdash; ideally create a reduced test case.
A good bug report shouldn't leave others needing to chase you up for more A good bug report shouldn't leave others needing to chase you up for more
information. Please try to be as detailed as possible in your report. What is information. Please try to be as detailed as possible in your report. What is
@ -209,9 +212,8 @@ Example:
> suitable, include the steps required to reproduce the bug. > suitable, include the steps required to reproduce the bug.
> >
> 1. This is the first step > 1. This is the first step
> 2. This is the second step > 1. This is the second step
> 3. Further steps, etc. > 1. Further steps, etc.
>
> >
> Any other information you want to share that is relevant to the issue being > Any other information you want to share that is relevant to the issue being
> reported. This might include the lines of code that you have identified as > reported. This might include the lines of code that you have identified as
@ -222,7 +224,7 @@ Example:
## Feature requests ## Feature requests
Feature requests are welcome. But take a moment to find out whether your idea Feature requests are welcome. But take a moment to find out whether your idea
fits with the scope and aims of the project. It's up to *you* to make a strong fits with the scope and aims of the project. It's up to _you_ to make a strong
case to convince the project's developers of the merits of this feature. Please case to convince the project's developers of the merits of this feature. Please
provide as much detail and context as possible. provide as much detail and context as possible.
@ -244,51 +246,51 @@ accurate comments, etc.) and any other requirements (such as test coverage).
Follow this process if you'd like your work considered for inclusion in the Follow this process if you'd like your work considered for inclusion in the
project: project:
1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork,
and configure the remotes: and configure the remotes:
```bash ```bash
# Clone your fork of the repo into the current directory # Clone your fork of the repo into the current directory
git clone https://github.com/<your-username>/<repo-name> git clone https://github.com/<your-username>/<repo-name>
# Navigate to the newly cloned directory # Navigate to the newly cloned directory
cd <repo-name> cd <repo-name>
# Assign the original repo to a remote called "upstream" # Assign the original repo to a remote called "upstream"
git remote add upstream https://github.com/<upstream-owner>/<repo-name> git remote add upstream https://github.com/<upstream-owner>/<repo-name>
``` ```
2. If you cloned a while ago, get the latest changes from upstream: 1. If you cloned a while ago, get the latest changes from upstream:
```bash ```bash
git checkout <dev-branch> git checkout <dev-branch>
git pull upstream <dev-branch> git pull upstream <dev-branch>
``` ```
3. Create a new topic branch (off the main project development branch) to 1. Create a new topic branch (off the main project development branch) to
contain your feature, change, or fix: contain your feature, change, or fix:
```bash ```bash
git checkout -b <topic-branch-name> git checkout -b <topic-branch-name>
``` ```
4. Commit your changes in logical chunks. Please adhere to these [git commit 1. Commit your changes in logical chunks. Please adhere to these [git commit
message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
or your code is unlikely be merged into the main project. Use Git's or your code is unlikely be merged into the main project. Use Git's
[interactive rebase](https://help.github.com/articles/interactive-rebase) [interactive rebase](https://help.github.com/articles/interactive-rebase)
feature to tidy up your commits before making them public. feature to tidy up your commits before making them public.
5. Locally merge (or rebase) the upstream development branch into your topic branch: 1. Locally merge (or rebase) the upstream development branch into your topic branch:
```bash ```bash
git pull [--rebase] upstream <dev-branch> git pull [--rebase] upstream <dev-branch>
``` ```
6. Push your topic branch up to your fork: 1. Push your topic branch up to your fork:
```bash ```bash
git push origin <topic-branch-name> git push origin <topic-branch-name>
``` ```
7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 1. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
with a clear title and description. with a clear title and description.
**IMPORTANT**: By submitting a patch, you agree to allow the project owner to **IMPORTANT**: By submitting a patch, you agree to allow the project owner to

@ -1,6 +1,6 @@
{ {
"name": "cvat-ui", "name": "cvat-ui",
"version": "1.2.2", "version": "1.3.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

@ -1,6 +1,6 @@
{ {
"name": "cvat-ui", "name": "cvat-ui",
"version": "1.2.2", "version": "1.3.0",
"description": "CVAT single-page application", "description": "CVAT single-page application",
"main": "src/index.tsx", "main": "src/index.tsx",
"scripts": { "scripts": {

@ -1288,19 +1288,23 @@ ThunkAction<Promise<void>, {}, {}, AnyAction> {
} }
export function changeLabelColorAsync( export function changeLabelColorAsync(
sessionInstance: any,
frameNumber: number,
label: any, label: any,
color: string, color: string,
): ThunkAction<Promise<void>, {}, {}, AnyAction> { ): ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => { return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try { try {
const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters(); const {
filters,
showAllInterpolationTracks,
jobInstance,
frame,
} = receiveAnnotationsParameters();
const updatedLabel = label; const updatedLabel = label;
updatedLabel.color = color; updatedLabel.color = color;
const states = await sessionInstance.annotations const states = await jobInstance.annotations
.get(frameNumber, showAllInterpolationTracks, filters); .get(frame, showAllInterpolationTracks, filters);
const history = await sessionInstance.actions.get(); const history = await jobInstance.actions.get();
dispatch({ dispatch({
type: AnnotationActionTypes.CHANGE_LABEL_COLOR_SUCCESS, type: AnnotationActionTypes.CHANGE_LABEL_COLOR_SUCCESS,

@ -65,6 +65,7 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
PASTE_SHAPE: keyMap.PASTE_SHAPE, PASTE_SHAPE: keyMap.PASTE_SHAPE,
SWITCH_DRAW_MODE: keyMap.SWITCH_DRAW_MODE, SWITCH_DRAW_MODE: keyMap.SWITCH_DRAW_MODE,
SWITCH_MERGE_MODE: keyMap.SWITCH_MERGE_MODE, SWITCH_MERGE_MODE: keyMap.SWITCH_MERGE_MODE,
SWITCH_SPLIT_MODE: keyMap.SWITCH_SPLIT_MODE,
SWITCH_GROUP_MODE: keyMap.SWITCH_GROUP_MODE, SWITCH_GROUP_MODE: keyMap.SWITCH_GROUP_MODE,
RESET_GROUP: keyMap.RESET_GROUP, RESET_GROUP: keyMap.RESET_GROUP,
CANCEL: keyMap.CANCEL, CANCEL: keyMap.CANCEL,
@ -102,6 +103,15 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
canvasInstance.merge({ enabled: !merging }); canvasInstance.merge({ enabled: !merging });
mergeObjects(!merging); mergeObjects(!merging);
}, },
SWITCH_SPLIT_MODE: (event: KeyboardEvent | undefined) => {
preventDefault(event);
const splitting = activeControl === ActiveControl.SPLIT;
if (!splitting) {
canvasInstance.cancel();
}
canvasInstance.split({ enabled: !splitting });
splitTrack(!splitting);
},
SWITCH_GROUP_MODE: (event: KeyboardEvent | undefined) => { SWITCH_GROUP_MODE: (event: KeyboardEvent | undefined) => {
preventDefault(event); preventDefault(event);
const grouping = activeControl === ActiveControl.GROUP; const grouping = activeControl === ActiveControl.GROUP;
@ -205,6 +215,7 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
/> />
<SplitControl <SplitControl
canvasInstance={canvasInstance} canvasInstance={canvasInstance}
switchSplitShortcut={normalizedKeyMap.SWITCH_SPLIT_MODE}
activeControl={activeControl} activeControl={activeControl}
splitTrack={splitTrack} splitTrack={splitTrack}
/> />

@ -4,7 +4,7 @@
import React from 'react'; import React from 'react';
import { Row, Col } from 'antd/lib/grid'; import { Row, Col } from 'antd/lib/grid';
import Select from 'antd/lib/select'; import Select, { OptionProps } from 'antd/lib/select';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import InputNumber from 'antd/lib/input-number'; import InputNumber from 'antd/lib/input-number';
import Radio, { RadioChangeEvent } from 'antd/lib/radio'; import Radio, { RadioChangeEvent } from 'antd/lib/radio';
@ -70,6 +70,15 @@ function DrawShapePopoverComponent(props: Props): JSX.Element {
<Row type='flex' justify='center'> <Row type='flex' justify='center'>
<Col span={24}> <Col span={24}>
<Select <Select
showSearch
filterOption={(input: string, option: React.ReactElement<OptionProps>) => {
const { children } = option.props;
if (typeof (children) === 'string') {
return children.toLowerCase().includes(input.toLowerCase());
}
return false;
}}
value={`${selectedLabeID}`} value={`${selectedLabeID}`}
onChange={onChangeLabel} onChange={onChangeLabel}
> >

@ -13,12 +13,13 @@ import { ActiveControl } from 'reducers/interfaces';
interface Props { interface Props {
canvasInstance: Canvas; canvasInstance: Canvas;
activeControl: ActiveControl; activeControl: ActiveControl;
switchSplitShortcut: string;
splitTrack(enabled: boolean): void; splitTrack(enabled: boolean): void;
} }
function SplitControl(props: Props): JSX.Element { function SplitControl(props: Props): JSX.Element {
const { const {
switchSplitShortcut,
activeControl, activeControl,
canvasInstance, canvasInstance,
splitTrack, splitTrack,
@ -40,7 +41,7 @@ function SplitControl(props: Props): JSX.Element {
}; };
return ( return (
<Tooltip title='Split a track' placement='right'> <Tooltip title={`Split a track ${switchSplitShortcut}`} placement='right'>
<Icon {...dynamicIconProps} component={SplitIcon} /> <Icon {...dynamicIconProps} component={SplitIcon} />
</Tooltip> </Tooltip>
); );

@ -5,17 +5,16 @@
import React from 'react'; import React from 'react';
import { Row, Col } from 'antd/lib/grid'; import { Row, Col } from 'antd/lib/grid';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import Text from 'antd/lib/typography/Text';
interface Props { interface Props {
shortcut: string;
colors: string[]; colors: string[];
onChange(color: string): void; onChange(color: string): void;
} }
function ColorChanger(props: Props): JSX.Element { function ColorChanger(props: Props): JSX.Element {
const { const { shortcut, colors, onChange } = props;
colors,
onChange,
} = props;
const cols = 6; const cols = 6;
const rows = Math.ceil(colors.length / cols); const rows = Math.ceil(colors.length / cols);
@ -47,9 +46,12 @@ function ColorChanger(props: Props): JSX.Element {
} }
return ( return (
<> <div>
<Text>
{`Press ${shortcut} to set a random color`}
</Text>
{antdRows} {antdRows}
</> </div>
); );
} }

@ -18,6 +18,7 @@ interface Props {
visible: boolean; visible: boolean;
statesHidden: boolean; statesHidden: boolean;
statesLocked: boolean; statesLocked: boolean;
changeColorShortcut: string;
hideStates(): void; hideStates(): void;
showStates(): void; showStates(): void;
lockStates(): void; lockStates(): void;
@ -38,6 +39,7 @@ function LabelItemComponent(props: Props): JSX.Element {
lockStates, lockStates,
unlockStates, unlockStates,
changeColor, changeColor,
changeColorShortcut,
} = props; } = props;
return ( return (
@ -54,6 +56,7 @@ function LabelItemComponent(props: Props): JSX.Element {
trigger='click' trigger='click'
content={( content={(
<ColorChanger <ColorChanger
shortcut={changeColorShortcut}
onChange={changeColor} onChange={changeColor}
colors={labelColors} colors={labelColors}
/> />

@ -5,7 +5,7 @@
import React from 'react'; import React from 'react';
import { Row, Col } from 'antd/lib/grid'; import { Row, Col } from 'antd/lib/grid';
import Icon from 'antd/lib/icon'; import Icon from 'antd/lib/icon';
import Select from 'antd/lib/select'; import Select, { OptionProps } from 'antd/lib/select';
import Radio, { RadioChangeEvent } from 'antd/lib/radio'; import Radio, { RadioChangeEvent } from 'antd/lib/radio';
import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox'; import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
@ -86,7 +86,7 @@ function ItemMenu(
{shapeType === ShapeType.CUBOID && ( {shapeType === ShapeType.CUBOID && (
<Menu.Item> <Menu.Item>
<Button type='link' onClick={resetCuboidPerspective}> <Button type='link' onClick={resetCuboidPerspective}>
<Icon component={ResetPerspectiveIcon}/> <Icon component={ResetPerspectiveIcon} />
Reset perspective Reset perspective
</Button> </Button>
</Menu.Item> </Menu.Item>
@ -200,7 +200,20 @@ function ItemTopComponent(props: ItemTopComponentProps): JSX.Element {
</Col> </Col>
<Col span={12}> <Col span={12}>
<Tooltip title='Change current label'> <Tooltip title='Change current label'>
<Select size='small' value={`${labelID}`} onChange={changeLabel}> <Select
size='small'
value={`${labelID}`}
onChange={changeLabel}
showSearch
filterOption={(input: string, option: React.ReactElement<OptionProps>) => {
const { children } = option.props;
if (typeof (children) === 'string') {
return children.toLowerCase().includes(input.toLowerCase());
}
return false;
}}
>
{ labels.map((label: any): JSX.Element => ( { labels.map((label: any): JSX.Element => (
<Select.Option key={label.id} value={`${label.id}`}> <Select.Option key={label.id} value={`${label.id}`}>
{label.name} {label.name}
@ -366,7 +379,7 @@ function ItemButtonsComponent(props: ItemButtonsComponentProps): JSX.Element {
<Col> <Col>
<Tooltip title={`Switch lock property ${switchLockShortcut}`}> <Tooltip title={`Switch lock property ${switchLockShortcut}`}>
{ locked { locked
? <Icon type='lock' onClick={unlock} theme='filled'/> ? <Icon type='lock' onClick={unlock} theme='filled' />
: <Icon type='unlock' onClick={lock} />} : <Icon type='unlock' onClick={lock} />}
</Tooltip> </Tooltip>
</Col> </Col>
@ -433,7 +446,7 @@ function ItemButtonsComponent(props: ItemButtonsComponentProps): JSX.Element {
<Col> <Col>
<Tooltip title={`Switch lock property ${switchLockShortcut}`}> <Tooltip title={`Switch lock property ${switchLockShortcut}`}>
{ locked { locked
? <Icon type='lock' onClick={unlock} theme='filled'/> ? <Icon type='lock' onClick={unlock} theme='filled' />
: <Icon type='unlock' onClick={lock} />} : <Icon type='unlock' onClick={lock} />}
</Tooltip> </Tooltip>
</Col> </Col>
@ -851,6 +864,7 @@ function ObjectItemComponent(props: Props): JSX.Element {
trigger='click' trigger='click'
content={( content={(
<ColorChanger <ColorChanger
shortcut={normalizedKeyMap.CHANGE_OBJECT_COLOR}
onChange={changeColor} onChange={changeColor}
colors={colors} colors={colors}
/> />

@ -23,6 +23,7 @@ interface StateToProps {
labelName: string; labelName: string;
labelColor: string; labelColor: string;
labelColors: string[]; labelColors: string[];
changeColorShortcut: string;
objectStates: any[]; objectStates: any[];
jobInstance: any; jobInstance: any;
frameNumber: any; frameNumber: any;
@ -30,7 +31,7 @@ interface StateToProps {
interface DispatchToProps { interface DispatchToProps {
updateAnnotations(states: any[]): void; updateAnnotations(states: any[]): void;
changeLabelColor(sessionInstance: any, frameNumber: number, label: any, color: string): void; changeLabelColor(label: any, color: string): void;
} }
function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps { function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
@ -50,6 +51,9 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
}, },
colors: labelColors, colors: labelColors,
}, },
shortcuts: {
normalizedKeyMap,
},
} = state; } = state;
const [label] = labels const [label] = labels
@ -63,6 +67,7 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
objectStates, objectStates,
jobInstance, jobInstance,
frameNumber, frameNumber,
changeColorShortcut: normalizedKeyMap.CHANGE_OBJECT_COLOR,
}; };
} }
@ -72,12 +77,10 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
dispatch(updateAnnotationsAsync(states)); dispatch(updateAnnotationsAsync(states));
}, },
changeLabelColor( changeLabelColor(
sessionInstance: any,
frameNumber: number,
label: any, label: any,
color: string, color: string,
): void { ): void {
dispatch(changeLabelColorAsync(sessionInstance, frameNumber, label, color)); dispatch(changeLabelColorAsync(label, color));
}, },
}; };
} }
@ -152,11 +155,9 @@ class LabelItemContainer extends React.PureComponent<Props, State> {
const { const {
changeLabelColor, changeLabelColor,
label, label,
frameNumber,
jobInstance,
} = this.props; } = this.props;
changeLabelColor(jobInstance, frameNumber, label, color); changeLabelColor(label, color);
}; };
private switchHidden(value: boolean): void { private switchHidden(value: boolean): void {
@ -196,6 +197,7 @@ class LabelItemContainer extends React.PureComponent<Props, State> {
labelName, labelName,
labelColor, labelColor,
labelColors, labelColors,
changeColorShortcut,
} = this.props; } = this.props;
return ( return (
@ -206,6 +208,7 @@ class LabelItemContainer extends React.PureComponent<Props, State> {
visible={visible} visible={visible}
statesHidden={statesHidden} statesHidden={statesHidden}
statesLocked={statesLocked} statesLocked={statesLocked}
changeColorShortcut={changeColorShortcut}
hideStates={this.hideStates} hideStates={this.hideStates}
showStates={this.showStates} showStates={this.showStates}
lockStates={this.lockStates} lockStates={this.lockStates}

@ -24,8 +24,7 @@ import {
} from 'actions/annotation-actions'; } from 'actions/annotation-actions';
import ObjectStateItemComponent from 'components/annotation-page/standard-workspace/objects-side-bar/object-item'; import ObjectStateItemComponent from 'components/annotation-page/standard-workspace/objects-side-bar/object-item';
import { shift } from 'utils/math';
import {shift} from 'utils/math';
interface OwnProps { interface OwnProps {
clientID: number; clientID: number;
@ -58,7 +57,7 @@ interface DispatchToProps {
removeObject: (sessionInstance: any, objectState: any) => void; removeObject: (sessionInstance: any, objectState: any) => void;
copyShape: (objectState: any) => void; copyShape: (objectState: any) => void;
propagateObject: (objectState: any) => void; propagateObject: (objectState: any) => void;
changeLabelColor(sessionInstance: any, frameNumber: number, label: any, color: string): void; changeLabelColor(label: any, color: string): void;
changeGroupColor(group: number, color: string): void; changeGroupColor(group: number, color: string): void;
} }
@ -155,12 +154,10 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
dispatch(propagateObjectAction(objectState)); dispatch(propagateObjectAction(objectState));
}, },
changeLabelColor( changeLabelColor(
sessionInstance: any,
frameNumber: number,
label: any, label: any,
color: string, color: string,
): void { ): void {
dispatch(changeLabelColorAsync(sessionInstance, frameNumber, label, color)); dispatch(changeLabelColorAsync(label, color));
}, },
changeGroupColor(group: number, color: string): void { changeGroupColor(group: number, color: string): void {
dispatch(changeGroupColorAsync(group, color)); dispatch(changeGroupColorAsync(group, color));
@ -357,12 +354,10 @@ class ObjectItemContainer extends React.PureComponent<Props> {
private changeColor = (color: string): void => { private changeColor = (color: string): void => {
const { const {
jobInstance,
objectState, objectState,
colorBy, colorBy,
changeLabelColor, changeLabelColor,
changeGroupColor, changeGroupColor,
frameNumber,
} = this.props; } = this.props;
if (colorBy === ColorBy.INSTANCE) { if (colorBy === ColorBy.INSTANCE) {
@ -371,7 +366,7 @@ class ObjectItemContainer extends React.PureComponent<Props> {
} else if (colorBy === ColorBy.GROUP) { } else if (colorBy === ColorBy.GROUP) {
changeGroupColor(objectState.group.id, color); changeGroupColor(objectState.group.id, color);
} else if (colorBy === ColorBy.LABEL) { } else if (colorBy === ColorBy.LABEL) {
changeLabelColor(jobInstance, frameNumber, objectState.label, color); changeLabelColor(objectState.label, color);
} }
}; };
@ -399,19 +394,13 @@ class ObjectItemContainer extends React.PureComponent<Props> {
this.commit(); this.commit();
}; };
private changeFrame(frame: number): void {
const { changeFrame, canvasInstance } = this.props;
if (isAbleToChangeFrame(canvasInstance)) {
changeFrame(frame);
}
}
private switchCuboidOrientation = (): void => { private switchCuboidOrientation = (): void => {
function cuboidOrientationIsLeft(points: number[]): boolean { function cuboidOrientationIsLeft(points: number[]): boolean {
return points[12] > points[0]; return points[12] > points[0];
} }
const {objectState} = this.props; const { objectState } = this.props;
this.resetCuboidPerspective(false); this.resetCuboidPerspective(false);
@ -419,19 +408,19 @@ class ObjectItemContainer extends React.PureComponent<Props> {
cuboidOrientationIsLeft(objectState.points) ? 4 : -4); cuboidOrientationIsLeft(objectState.points) ? 4 : -4);
this.commit(); this.commit();
} };
private resetCuboidPerspective = (commit: boolean = true): void => { private resetCuboidPerspective = (commit = true): void => {
function cuboidOrientationIsLeft(points: number[]): boolean { function cuboidOrientationIsLeft(points: number[]): boolean {
return points[12] > points[0]; return points[12] > points[0];
} }
const {objectState} = this.props; const { objectState } = this.props;
const {points} = objectState; const { points } = objectState;
const minD = { const minD = {
x: (points[6] - points[2])*0.001, x: (points[6] - points[2]) * 0.001,
y: (points[3] - points[1])*0.001, y: (points[3] - points[1]) * 0.001,
} };
if (cuboidOrientationIsLeft(points)) { if (cuboidOrientationIsLeft(points)) {
points[14] = points[10] + points[2] - points[6] + minD.x; points[14] = points[10] + points[2] - points[6] + minD.x;
@ -451,6 +440,13 @@ class ObjectItemContainer extends React.PureComponent<Props> {
objectState.points = points; objectState.points = points;
if (commit) this.commit(); if (commit) this.commit();
};
private changeFrame(frame: number): void {
const { changeFrame, canvasInstance } = this.props;
if (isAbleToChangeFrame(canvasInstance)) {
changeFrame(frame);
}
} }
private commit(): void { private commit(): void {

@ -14,9 +14,16 @@ import {
collapseObjectItems, collapseObjectItems,
copyShape as copyShapeAction, copyShape as copyShapeAction,
propagateObject as propagateObjectAction, propagateObject as propagateObjectAction,
changeGroupColorAsync,
changeLabelColorAsync,
} from 'actions/annotation-actions'; } from 'actions/annotation-actions';
import { Canvas, isAbleToChangeFrame } from 'cvat-canvas-wrapper'; import { Canvas, isAbleToChangeFrame } from 'cvat-canvas-wrapper';
import { CombinedState, StatesOrdering, ObjectType } from 'reducers/interfaces'; import {
CombinedState,
StatesOrdering,
ObjectType,
ColorBy,
} from 'reducers/interfaces';
interface StateToProps { interface StateToProps {
jobInstance: any; jobInstance: any;
@ -27,6 +34,8 @@ interface StateToProps {
statesCollapsed: boolean; statesCollapsed: boolean;
objectStates: any[]; objectStates: any[];
annotationsFilters: string[]; annotationsFilters: string[];
colors: string[];
colorBy: ColorBy;
activatedStateID: number | null; activatedStateID: number | null;
minZLayer: number; minZLayer: number;
maxZLayer: number; maxZLayer: number;
@ -43,6 +52,8 @@ interface DispatchToProps {
copyShape: (objectState: any) => void; copyShape: (objectState: any) => void;
propagateObject: (objectState: any) => void; propagateObject: (objectState: any) => void;
changeFrame(frame: number): void; changeFrame(frame: number): void;
changeGroupColor(group: number, color: string): void;
changeLabelColor(label: any, color: string): void;
} }
function mapStateToProps(state: CombinedState): StateToProps { function mapStateToProps(state: CombinedState): StateToProps {
@ -71,6 +82,12 @@ function mapStateToProps(state: CombinedState): StateToProps {
instance: canvasInstance, instance: canvasInstance,
}, },
tabContentHeight: listHeight, tabContentHeight: listHeight,
colors,
},
settings: {
shapes: {
colorBy,
},
}, },
shortcuts: { shortcuts: {
keyMap, keyMap,
@ -103,6 +120,8 @@ function mapStateToProps(state: CombinedState): StateToProps {
frameNumber, frameNumber,
jobInstance, jobInstance,
annotationsFilters, annotationsFilters,
colors,
colorBy,
activatedStateID, activatedStateID,
minZLayer, minZLayer,
maxZLayer, maxZLayer,
@ -133,6 +152,12 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
changeFrame(frame: number): void { changeFrame(frame: number): void {
dispatch(changeFrameAsync(frame)); dispatch(changeFrameAsync(frame));
}, },
changeGroupColor(group: number, color: string): void {
dispatch(changeGroupColorAsync(group, color));
},
changeLabelColor(label: any, color: string): void {
dispatch(changeLabelColorAsync(label, color));
},
}; };
} }
@ -252,6 +277,8 @@ class ObjectsListContainer extends React.PureComponent<Props, State> {
objectStates, objectStates,
jobInstance, jobInstance,
updateAnnotations, updateAnnotations,
changeGroupColor,
changeLabelColor,
removeObject, removeObject,
copyShape, copyShape,
propagateObject, propagateObject,
@ -261,6 +288,8 @@ class ObjectsListContainer extends React.PureComponent<Props, State> {
keyMap, keyMap,
normalizedKeyMap, normalizedKeyMap,
canvasInstance, canvasInstance,
colors,
colorBy,
} = this.props; } = this.props;
const { const {
sortedStatesID, sortedStatesID,
@ -282,6 +311,7 @@ class ObjectsListContainer extends React.PureComponent<Props, State> {
PROPAGATE_OBJECT: keyMap.PROPAGATE_OBJECT, PROPAGATE_OBJECT: keyMap.PROPAGATE_OBJECT,
NEXT_KEY_FRAME: keyMap.NEXT_KEY_FRAME, NEXT_KEY_FRAME: keyMap.NEXT_KEY_FRAME,
PREV_KEY_FRAME: keyMap.PREV_KEY_FRAME, PREV_KEY_FRAME: keyMap.PREV_KEY_FRAME,
CHANGE_OBJECT_COLOR: keyMap.CHANGE_OBJECT_COLOR,
}; };
const preventDefault = (event: KeyboardEvent | undefined): void => { const preventDefault = (event: KeyboardEvent | undefined): void => {
@ -359,6 +389,27 @@ class ObjectsListContainer extends React.PureComponent<Props, State> {
removeObject(jobInstance, state, event ? event.shiftKey : false); removeObject(jobInstance, state, event ? event.shiftKey : false);
} }
}, },
CHANGE_OBJECT_COLOR: (event: KeyboardEvent | undefined) => {
preventDefault(event);
const state = activatedStated();
if (state) {
if (colorBy === ColorBy.GROUP) {
const colorID = (colors.indexOf(state.group.color) + 1) % colors.length;
changeGroupColor(state.group.id, colors[colorID]);
return;
}
if (colorBy === ColorBy.LABEL) {
const colorID = (colors.indexOf(state.label.color) + 1) % colors.length;
changeLabelColor(state.label, colors[colorID]);
return;
}
const colorID = (colors.indexOf(state.color) + 1) % colors.length;
state.color = colors[colorID];
updateAnnotations([state]);
}
},
TO_BACKGROUND: (event: KeyboardEvent | undefined) => { TO_BACKGROUND: (event: KeyboardEvent | undefined) => {
preventDefault(event); preventDefault(event);
const state = activatedStated(); const state = activatedStated();

@ -217,6 +217,12 @@ const defaultKeyMap = {
sequences: ['m'], sequences: ['m'],
action: 'keydown', action: 'keydown',
}, },
SWITCH_SPLIT_MODE: {
name: 'Split mode',
description: 'Activate or deactivate mode to splitting shapes',
sequences: ['alt+m'],
action: 'keydown',
},
SWITCH_GROUP_MODE: { SWITCH_GROUP_MODE: {
name: 'Group mode', name: 'Group mode',
description: 'Activate or deactivate mode to grouping shapes', description: 'Activate or deactivate mode to grouping shapes',
@ -320,6 +326,12 @@ const defaultKeyMap = {
sequences: ['Control'], sequences: ['Control'],
action: 'keydown', action: 'keydown',
}, },
CHANGE_OBJECT_COLOR: {
name: 'Change color',
description: 'Set the next color for an activated shape',
sequences: ['Enter'],
action: 'keydown',
},
} as any as Record<string, ExtendedKeyMapOptions>; } as any as Record<string, ExtendedKeyMapOptions>;

Loading…
Cancel
Save