未验证 提交 03f31128 编写于 作者: P Peter Pan 提交者: GitHub

minor improvements of hyper-parameter page (#964)

* fix: text overflow ellipsis in hyper-parameter page

* fix: minor bugs fix

* fix: minor bugs fix

* fix: error when high-dimensional list is empty
上级 d946813b
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
"hparams": "Hyper Parameters", "hparams": "Hyper Parameters",
"metrics": "Metrics" "metrics": "Metrics"
}, },
"empty": "<0>No Hparams Data was Found.</0><1>You can try the following solutions:</1><2><0>make sure that you have used `add_scalar` to record the metrics.</0><0>make sure that the `metrics_list` of `add_hparams` includes the `tag` of `add_scalar`.</0></2><3>For detailed tutorials, please refer to the <1>VisualDL Hparams Instructions</1>.</3>",
"hyper-parameter": { "hyper-parameter": {
"order-default": "Default" "order-default": "Default"
}, },
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
"hparams": "超参数", "hparams": "超参数",
"metrics": "度量指标" "metrics": "度量指标"
}, },
"empty": "<0>无超参可视化结果展示</0><1>参考以下几种解决方案:</1><2><0>请确保您已使用 `add_scalar` 接口记录所需展示的模型度量指标(metrics)。</0><0>请确保 `add_hparams` 接口的 `metrics_list` 参数中包含 `add_scalar` 接口的 `tag` 参数。</0></2><3>您可以参考<1>Hparams使用文档</1>获取详细教程。</3>",
"hyper-parameter": { "hyper-parameter": {
"order-default": "默认" "order-default": "默认"
}, },
......
...@@ -40,7 +40,7 @@ const Wrapper = styled.div` ...@@ -40,7 +40,7 @@ const Wrapper = styled.div`
} }
> .inner { > .inner {
width: calc(50% - ${rem(280)}); width: max(calc(50% - ${rem(280)}), ${rem(280)});
color: var(--text-light-color); color: var(--text-light-color);
${transitionProps('color')} ${transitionProps('color')}
${link} ${link}
...@@ -56,7 +56,8 @@ const Wrapper = styled.div` ...@@ -56,7 +56,8 @@ const Wrapper = styled.div`
margin: 0; margin: 0;
} }
ol { ol,
ul {
padding-left: 2em; padding-left: 2em;
line-height: 1.857142857; line-height: 1.857142857;
} }
...@@ -116,7 +117,7 @@ const Error: FunctionComponent<WithStyled> = ({className, children}) => { ...@@ -116,7 +117,7 @@ const Error: FunctionComponent<WithStyled> = ({className, children}) => {
</li> </li>
<li> <li>
<Trans i18nKey="errors:common.3"> <Trans i18nKey="errors:common.3">
Log files are generated and data is writte. Please try to&nbsp; Log files are generated and data is written. Please try to&nbsp;
<a onClick={reload}>Refresh</a>. <a onClick={reload}>Refresh</a>.
</Trans> </Trans>
</li> </li>
......
...@@ -195,6 +195,9 @@ const HistogramChart: FunctionComponent<HistogramChartProps> = ({run, tag, mode, ...@@ -195,6 +195,9 @@ const HistogramChart: FunctionComponent<HistogramChartProps> = ({run, tag, mode,
xAxis: { xAxis: {
axisPointer: { axisPointer: {
snap: mode === Modes.Overlay snap: mode === Modes.Overlay
},
splitLine: {
show: false
} }
}, },
yAxis: { yAxis: {
......
/**
* Copyright 2020 Baidu Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, {FunctionComponent} from 'react';
import {Trans, useTranslation} from 'react-i18next';
import Error from '~/components/Error';
const DocumentMap: Record<string, string> = {
zh: 'https://github.com/PaddlePaddle/VisualDL/blob/develop/docs/components/README_CN.md#Hparams--超参可视化',
en: 'https://github.com/PaddlePaddle/VisualDL/tree/develop/docs/components#Hparams--HyperParameters-Visualization'
};
const Empty: FunctionComponent = () => {
const {i18n} = useTranslation('hyper-parameter');
return (
<Error>
<Trans i18nKey="hyper-parameter:empty">
<h4>No Hparams Data was Found.</h4>
<p>You can try the following solutions:</p>
<ul>
<li>make sure that you have used `add_scalar` to record the metrics.</li>
<li>make sure that the `metrics_list` of `add_hparams` includes the `tag` of `add_scalar`.</li>
</ul>
<p>
For detailed tutorials, please refer to the&nbsp;
<a
href={DocumentMap[i18n.language || String(i18n.options.fallbackLng)] ?? DocumentMap.en}
target="_blank"
rel="noreferrer"
>
VisualDL Hparams Instructions
</a>
.
</p>
</Trans>
</Error>
);
};
export default Empty;
...@@ -18,6 +18,7 @@ import React, {FunctionComponent, useCallback, useEffect, useRef, useState} from ...@@ -18,6 +18,7 @@ import React, {FunctionComponent, useCallback, useEffect, useRef, useState} from
import NumberInput from './NumberInput'; import NumberInput from './NumberInput';
import type {Range} from '~/resource/hyper-parameter'; import type {Range} from '~/resource/hyper-parameter';
import {rem} from '~/utils/style';
import styled from 'styled-components'; import styled from 'styled-components';
import {useTranslation} from 'react-i18next'; import {useTranslation} from 'react-i18next';
...@@ -37,6 +38,8 @@ const Row = styled.div` ...@@ -37,6 +38,8 @@ const Row = styled.div`
flex-grow: 0; flex-grow: 0;
flex-shrink: 0; flex-shrink: 0;
margin-right: 1em; margin-right: 1em;
text-align: right;
min-width: ${rem(36)};
} }
`; `;
......
...@@ -17,9 +17,15 @@ ...@@ -17,9 +17,15 @@
import React, {FunctionComponent} from 'react'; import React, {FunctionComponent} from 'react';
import type {CellProps} from 'react-table'; import type {CellProps} from 'react-table';
import {ellipsis} from '~/utils/style';
import styled from 'styled-components';
const Span = styled.span`
${ellipsis()}
`;
const Cell = <D extends Record<string, unknown>>({cell}: CellProps<D>): ReturnType<FunctionComponent> => { const Cell = <D extends Record<string, unknown>>({cell}: CellProps<D>): ReturnType<FunctionComponent> => {
return <span>{cell.value}</span>; return <Span title={cell.value}>{cell.value}</Span>;
}; };
export default Cell; export default Cell;
...@@ -19,6 +19,12 @@ import React, {FunctionComponent} from 'react'; ...@@ -19,6 +19,12 @@ import React, {FunctionComponent} from 'react';
import {Resizer} from '~/components/Table'; import {Resizer} from '~/components/Table';
import type {HeaderProps as TableHeaderProps} from 'react-table'; import type {HeaderProps as TableHeaderProps} from 'react-table';
import type {WithStyled} from '~/utils/style'; import type {WithStyled} from '~/utils/style';
import {ellipsis} from '~/utils/style';
import styled from 'styled-components';
const Span = styled.span`
${ellipsis()}
`;
const Header: FunctionComponent<TableHeaderProps<Record<string, unknown>> & WithStyled> = ({ const Header: FunctionComponent<TableHeaderProps<Record<string, unknown>> & WithStyled> = ({
column, column,
...@@ -27,7 +33,9 @@ const Header: FunctionComponent<TableHeaderProps<Record<string, unknown>> & With ...@@ -27,7 +33,9 @@ const Header: FunctionComponent<TableHeaderProps<Record<string, unknown>> & With
}) => { }) => {
return ( return (
<> <>
<span className={className}>{children ?? column.id}</span> <Span className={className} title={children ? ('string' === typeof children ? children : '') : column.id}>
{children ?? column.id}
</Span>
{column.canResize ? ( {column.canResize ? (
<Resizer {...column.getResizerProps()} className={column.isResizing ? 'is-resizing' : ''} /> <Resizer {...column.getResizerProps()} className={column.isResizing ? 'is-resizing' : ''} />
) : null} ) : null}
......
...@@ -18,11 +18,22 @@ import React, {FunctionComponent} from 'react'; ...@@ -18,11 +18,22 @@ import React, {FunctionComponent} from 'react';
import type {CellProps} from 'react-table'; import type {CellProps} from 'react-table';
import {Expander} from '~/components/Table'; import {Expander} from '~/components/Table';
import {ellipsis} from '~/utils/style';
import styled from 'styled-components'; import styled from 'styled-components';
const Cell = styled.span` const Cell = styled.span`
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
max-width: 100%;
> ${Expander} {
flex: none;
}
> .cell {
flex: auto;
${ellipsis()}
}
`; `;
const ExpandableCell = <D extends Record<string, unknown>>({ const ExpandableCell = <D extends Record<string, unknown>>({
...@@ -32,7 +43,9 @@ const ExpandableCell = <D extends Record<string, unknown>>({ ...@@ -32,7 +43,9 @@ const ExpandableCell = <D extends Record<string, unknown>>({
return ( return (
<Cell> <Cell>
<Expander {...row.getToggleRowExpandedProps()} isExpanded={row.isExpanded} /> <Expander {...row.getToggleRowExpandedProps()} isExpanded={row.isExpanded} />
<span>{cell.value}</span> <span className="cell" title={cell.value}>
{cell.value}
</span>
</Cell> </Cell>
); );
}; };
......
...@@ -37,6 +37,8 @@ import {useSticky} from 'react-table-sticky'; ...@@ -37,6 +37,8 @@ import {useSticky} from 'react-table-sticky';
type TableViewTableProps = ViewData & { type TableViewTableProps = ViewData & {
sortBy?: SortingRule<string>[]; sortBy?: SortingRule<string>[];
columnOrder?: string[];
onOrderChange?: (order: string[]) => unknown;
expand?: boolean; expand?: boolean;
expandAll?: boolean; expandAll?: boolean;
}; };
...@@ -46,6 +48,8 @@ const TableViewTable: FunctionComponent<TableViewTableProps> = ({ ...@@ -46,6 +48,8 @@ const TableViewTable: FunctionComponent<TableViewTableProps> = ({
indicators, indicators,
list: data, list: data,
sortBy, sortBy,
columnOrder,
onOrderChange,
expand, expand,
expandAll expandAll
}) => { }) => {
...@@ -73,12 +77,15 @@ const TableViewTable: FunctionComponent<TableViewTableProps> = ({ ...@@ -73,12 +77,15 @@ const TableViewTable: FunctionComponent<TableViewTableProps> = ({
accessor: `${group}.${name}` as IndicatorGroup, // fix react-table's type error accessor: `${group}.${name}` as IndicatorGroup, // fix react-table's type error
id: name, id: name,
Header: group === 'metrics' ? MetricsHeader : Header, Header: group === 'metrics' ? MetricsHeader : Header,
Cell,
minWidth: 200 minWidth: 200
})) }))
], ],
[expand, indicators] [expand, indicators]
); );
const order = useMemo(() => (columnOrder ? ['name', ...columnOrder] : []), [columnOrder]);
const { const {
getTableProps, getTableProps,
headerGroups, headerGroups,
...@@ -96,7 +103,8 @@ const TableViewTable: FunctionComponent<TableViewTableProps> = ({ ...@@ -96,7 +103,8 @@ const TableViewTable: FunctionComponent<TableViewTableProps> = ({
data, data,
defaultColumn, defaultColumn,
initialState: { initialState: {
sortBy: sortBy ?? [] sortBy: sortBy ?? [],
columnOrder: order
}, },
autoResetExpanded: false autoResetExpanded: false
}, },
...@@ -127,6 +135,9 @@ const TableViewTable: FunctionComponent<TableViewTableProps> = ({ ...@@ -127,6 +135,9 @@ const TableViewTable: FunctionComponent<TableViewTableProps> = ({
() => (state.columnOrder.length ? state.columnOrder : tableColumns.map(c => c.id)), () => (state.columnOrder.length ? state.columnOrder : tableColumns.map(c => c.id)),
[state.columnOrder, tableColumns] [state.columnOrder, tableColumns]
); );
useEffect(() => {
onOrderChange?.(orderedColumnIds.filter(id => id !== 'name'));
}, [onOrderChange, orderedColumnIds]);
const droppableColumnId = useMemo(() => { const droppableColumnId = useMemo(() => {
if (draggingColumnId != null && droppableColumn != null) { if (draggingColumnId != null && droppableColumn != null) {
const [id, side] = droppableColumn; const [id, side] = droppableColumn;
...@@ -144,16 +155,16 @@ const TableViewTable: FunctionComponent<TableViewTableProps> = ({ ...@@ -144,16 +155,16 @@ const TableViewTable: FunctionComponent<TableViewTableProps> = ({
draggingColumnId != null && draggingColumnId != null &&
droppableColumn && droppableColumn &&
droppableColumn[1] === 'before' && droppableColumn[1] === 'before' &&
tableColumns[0]?.id === droppableColumn[0], orderedColumnIds[0] === droppableColumn[0],
[draggingColumnId, droppableColumn, tableColumns] [draggingColumnId, droppableColumn, orderedColumnIds]
); );
const isTableDroppableRight = useMemo( const isTableDroppableRight = useMemo(
() => () =>
draggingColumnId != null && draggingColumnId != null &&
droppableColumn && droppableColumn &&
droppableColumn[1] === 'after' && droppableColumn[1] === 'after' &&
tableColumns[tableColumns.length - 1]?.id === droppableColumn[0], orderedColumnIds[orderedColumnIds.length - 1] === droppableColumn[0],
[draggingColumnId, droppableColumn, tableColumns] [draggingColumnId, droppableColumn, orderedColumnIds]
); );
const drop = useCallback( const drop = useCallback(
(id: string, side: 'before' | 'after') => { (id: string, side: 'before' | 'after') => {
......
...@@ -15,16 +15,19 @@ ...@@ -15,16 +15,19 @@
*/ */
import {DEFAULT_ORDER_INDICATOR, OrderDirection} from '~/resource/hyper-parameter'; import {DEFAULT_ORDER_INDICATOR, OrderDirection} from '~/resource/hyper-parameter';
import React, {FunctionComponent, useMemo, useState} from 'react'; import React, {FunctionComponent, useCallback, useMemo, useState} from 'react';
import Select from '~/components/Select'; import Select from '~/components/Select';
import Table from './Table'; import Table from './Table';
import View from '~/components/HyperParameterPage/View'; import View from '~/components/HyperParameterPage/View';
import type {ViewData} from '~/resource/hyper-parameter'; import type {ViewData} from '~/resource/hyper-parameter';
import {rem} from '~/utils/style'; import {rem} from '~/utils/style';
import {safeSplit} from '~/utils';
import styled from 'styled-components'; import styled from 'styled-components';
import {useTranslation} from 'react-i18next'; import {useTranslation} from 'react-i18next';
const TABLE_ORDER_STORAGE_KEY = 'hyper-parameter-table-view-table-order';
const Wrapper = styled(View)` const Wrapper = styled(View)`
display: flex; display: flex;
width: 100%; width: 100%;
...@@ -91,6 +94,18 @@ const TableView: FunctionComponent<TableViewProps> = ({indicators, list, data}) ...@@ -91,6 +94,18 @@ const TableView: FunctionComponent<TableViewProps> = ({indicators, list, data})
[orderDirection, indicatorOrder] [orderDirection, indicatorOrder]
); );
const [columnOrder, setColumnOrder] = useState<string[]>(
safeSplit(window.sessionStorage.getItem(TABLE_ORDER_STORAGE_KEY) ?? '', ',')
);
const changeOrder = useCallback(
(order: string[]) => {
const filterOrder = order.filter(o => indicatorNameList.includes(o));
setColumnOrder(filterOrder);
window.sessionStorage.setItem(TABLE_ORDER_STORAGE_KEY, filterOrder.join(','));
},
[indicatorNameList]
);
return ( return (
<Wrapper> <Wrapper>
<OrderSection> <OrderSection>
...@@ -114,7 +129,15 @@ const TableView: FunctionComponent<TableViewProps> = ({indicators, list, data}) ...@@ -114,7 +129,15 @@ const TableView: FunctionComponent<TableViewProps> = ({indicators, list, data})
) : null} ) : null}
</OrderSection> </OrderSection>
<TableSection> <TableSection>
<Table indicators={indicators} list={list} data={data} sortBy={sortBy} expand /> <Table
indicators={indicators}
list={list}
data={data}
sortBy={sortBy}
expand
columnOrder={columnOrder}
onOrderChange={changeOrder}
/>
</TableSection> </TableSection>
</Wrapper> </Wrapper>
); );
......
...@@ -216,10 +216,6 @@ const Select = <T extends unknown>({ ...@@ -216,10 +216,6 @@ const Select = <T extends unknown>({
[multiple, propValue] [multiple, propValue]
); );
const isSelected = useMemo(
() => !!(multiple ? (value as T[]) && (value as T[]).length !== 0 : value != (null as T)),
[multiple, value]
);
const changeValue = useCallback( const changeValue = useCallback(
({value: mutateValue, disabled}: SelectListItem<T>) => { ({value: mutateValue, disabled}: SelectListItem<T>) => {
if (disabled) { if (disabled) {
...@@ -265,6 +261,14 @@ const Select = <T extends unknown>({ ...@@ -265,6 +261,14 @@ const Select = <T extends unknown>({
); );
const isListEmpty = useMemo(() => list.length === 0, [list]); const isListEmpty = useMemo(() => list.length === 0, [list]);
const isSelected = useMemo(
() =>
!!(multiple
? (value as T[]) && (value as T[]).length !== 0
: !(value == (null as T) || list.findIndex(i => i.value === value) === -1)),
[list, multiple, value]
);
const findLabelByValue = useCallback((v: T) => list.find(item => item.value === v)?.label ?? '', [list]); const findLabelByValue = useCallback((v: T) => list.find(item => item.value === v)?.label ?? '', [list]);
const label = useMemo( const label = useMemo(
() => () =>
......
...@@ -311,6 +311,9 @@ const HighDimensional: FunctionComponent = () => { ...@@ -311,6 +311,9 @@ const HighDimensional: FunctionComponent = () => {
setMetadata(data.metadata); setMetadata(data.metadata);
} else if (data !== null) { } else if (data !== null) {
setLoadingPhase('parsing'); setLoadingPhase('parsing');
} else {
setLoading(false);
setLoadingPhase('');
} }
}, [result, showError]); }, [result, showError]);
const hasVector = useMemo(() => dim !== 0, [dim]); const hasVector = useMemo(() => dim !== 0, [dim]);
......
...@@ -23,8 +23,9 @@ import {filter, format, formatIndicators} from '~/resource/hyper-parameter'; ...@@ -23,8 +23,9 @@ import {filter, format, formatIndicators} from '~/resource/hyper-parameter';
import BodyLoading from '~/components/BodyLoading'; import BodyLoading from '~/components/BodyLoading';
import Button from '~/components/Button'; import Button from '~/components/Button';
import Content from '~/components/Content'; import Content from '~/components/Content';
import Empty from '~/components/HyperParameterPage/Empty';
import Field from '~/components/Field'; import Field from '~/components/Field';
import ImportanceDialog from '~/components/HyperParameterPage/ImportanceDialog'; // import ImportanceDialog from '~/components/HyperParameterPage/ImportanceDialog';
import IndicatorFilter from '~/components/HyperParameterPage/IndicatorFilter/IndicatorFilter'; import IndicatorFilter from '~/components/HyperParameterPage/IndicatorFilter/IndicatorFilter';
import ParallelCoordinatesView from '~/components/HyperParameterPage/ParallelCoordinatesView'; import ParallelCoordinatesView from '~/components/HyperParameterPage/ParallelCoordinatesView';
import ScatterPlotMatrixView from '~/components/HyperParameterPage/ScatterPlotMatrixView'; import ScatterPlotMatrixView from '~/components/HyperParameterPage/ScatterPlotMatrixView';
...@@ -41,11 +42,13 @@ import {useTranslation} from 'react-i18next'; ...@@ -41,11 +42,13 @@ import {useTranslation} from 'react-i18next';
// width: 100%; // width: 100%;
// `; // `;
const HParamsImportanceDialog = styled(ImportanceDialog)` // const HParamsImportanceDialog = styled(ImportanceDialog)`
position: fixed; // position: fixed;
right: calc(${asideWidth} + ${rem(20)}); // right: calc(${asideWidth} + ${rem(20)});
bottom: ${rem(20)}; // bottom: ${rem(20)};
`; // `;
// NOTICE: remove it!!!
asideWidth;
const DownloadButtons = styled.div` const DownloadButtons = styled.div`
display: flex; display: flex;
...@@ -79,6 +82,14 @@ const HyperParameter: FunctionComponent = () => { ...@@ -79,6 +82,14 @@ const HyperParameter: FunctionComponent = () => {
const {t} = useTranslation(['hyper-parameter', 'common']); const {t} = useTranslation(['hyper-parameter', 'common']);
const {data: indicatorsData, loading: loadingIndicators} = useRequest<IndicatorData>('/hparams/indicators'); const {data: indicatorsData, loading: loadingIndicators} = useRequest<IndicatorData>('/hparams/indicators');
const isEmpty = useMemo(() => {
if (indicatorsData) {
const {hparams, metrics} = indicatorsData;
return hparams.length === 0 || metrics.length === 0;
}
return true;
}, [indicatorsData]);
const indicators = useMemo( const indicators = useMemo(
() => [ () => [
...formatIndicators(indicatorsData?.hparams ?? [], 'hparams'), ...formatIndicators(indicatorsData?.hparams ?? [], 'hparams'),
...@@ -128,7 +139,7 @@ const HyperParameter: FunctionComponent = () => { ...@@ -128,7 +139,7 @@ const HyperParameter: FunctionComponent = () => {
} }
}, [tabView, viewData]); }, [tabView, viewData]);
const [importanceDialogVisible, setImportanceDialogVisible] = useState(false); // const [importanceDialogVisible, setImportanceDialogVisible] = useState(false);
const downloadData = useCallback( const downloadData = useCallback(
(type: 'tsv' | 'csv') => (type: 'tsv' | 'csv') =>
...@@ -188,11 +199,11 @@ const HyperParameter: FunctionComponent = () => { ...@@ -188,11 +199,11 @@ const HyperParameter: FunctionComponent = () => {
<HPWrapper> <HPWrapper>
<Tab list={tabs} value={tabView} onChange={setTabView} /> <Tab list={tabs} value={tabView} onChange={setTabView} />
<ViewWrapper> <ViewWrapper>
{view} {isEmpty ? <Empty /> : view}
<HParamsImportanceDialog {/* <HParamsImportanceDialog
visible={importanceDialogVisible} visible={importanceDialogVisible}
onClickClose={() => setImportanceDialogVisible(false)} onClickClose={() => setImportanceDialogVisible(false)}
/> /> */}
</ViewWrapper> </ViewWrapper>
</HPWrapper> </HPWrapper>
</Content> </Content>
......
...@@ -183,6 +183,10 @@ export const GlobalStyle = createGlobalStyle` ...@@ -183,6 +183,10 @@ export const GlobalStyle = createGlobalStyle`
${transitionProps(['background-color', 'color'])} ${transitionProps(['background-color', 'color'])}
} }
body {
overflow-anchor: none;
}
a { a {
text-decoration: none; text-decoration: none;
color: inherit; color: inherit;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册