123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- part of 'control_board.dart';
- class _ProgressBar extends StatefulWidget {
- @override
- State<StatefulWidget> createState() => _ProgressBarState();
- }
- class _ProgressBarState extends State<_ProgressBar> {
- final playerController = Get.find<IPlayerController>() as VidPlayerController;
- /// 测量AI数据
- final measureData = Get.find<MeasureDataController>();
- /// ai结果
- late final List<AIDiagnosisPerImageDTO> aiResult = [];
- late List<double> aiKeyFrame = [];
- late DiagnosisOrganEnum diagnosisOrgan = DiagnosisOrganEnum.Null;
- double curCursorIndex = -10;
- /// 获取关键帧
- void getKeyFrame() {
- bool canShowAI = [
- DiagnosisConclusionEnum.Benign,
- DiagnosisConclusionEnum.Malignant,
- DiagnosisConclusionEnum.BenignAndMalignant
- ].contains(measureData.diagnosisConclusion);
- if (canShowAI) {
- final measureDataAIResults = jsonDecode(
- measureData.aiResults,
- );
- aiResult.clear();
- for (int i = 0; i < (measureDataAIResults as List).length; i++) {
- aiResult.add(
- AIDiagnosisPerImageDTO.fromJson(
- measureDataAIResults[i],
- ),
- );
- }
- for (int j = 0; j < aiResult.length; j++) {
- List<AIDiagnosisResultPerOrgan> diagResultsForEachOrgan =
- aiResult[j].diagResultsForEachOrgan ?? [];
- if (diagResultsForEachOrgan.isNotEmpty) {
- List<AIDetectedObject> detectedObjects =
- diagResultsForEachOrgan[0].detectedObjects ?? [];
- diagnosisOrgan = diagResultsForEachOrgan[0].organ;
- for (int m = 0; m < detectedObjects.length; m++) {
- if (detectedObjects[m].label != 0) {
- aiKeyFrame.add(j.toDouble());
- }
- }
- }
- }
- }
- }
- void onMeasuredAIResultsInfoChanged(Object sender, String e) {
- aiKeyFrame = [];
- if (e.isNotEmpty && e != "[]") {
- getKeyFrame();
- } else {
- setState(() {
- aiResult.clear();
- });
- }
- }
- @override
- void initState() {
- WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
- if (mounted) {
- playerController.eventHandler.addListener(onControllerEvent);
- getKeyFrame();
- }
- });
- measureData.aiResultsInfoChanged
- .addListener(onMeasuredAIResultsInfoChanged);
- super.initState();
- }
- @override
- void dispose() {
- measureData.aiResultsInfoChanged
- .removeListener(onMeasuredAIResultsInfoChanged);
- super.dispose();
- }
- @override
- Widget build(BuildContext context) {
- final controller = playerController;
- var index = controller.currentFrameIndex.toDouble();
- var max = 100.0;
- if (index < 0) {
- index = 0;
- } else {
- max = controller.totalFramesCount.toDouble();
- }
- return SliderTheme(
- data: const SliderThemeData(
- trackHeight: 12,
- thumbColor: Colors.white,
- trackShape: _FullWidthTrackShape(),
- ),
- child: LayoutBuilder(builder:
- (BuildContext layoutBuilderContext, BoxConstraints constraints) {
- return Stack(
- alignment: Alignment.center,
- children: [
- Slider(
- max: max,
- value: index,
- onChanged: (v) {
- controller.pause();
- controller.gotoFrame(v.toInt());
- },
- ),
- ...aiKeyFrame.map((i) {
- return _PanelPoint(
- max: max,
- index: index,
- constraints: constraints,
- currentIndex: i,
- onChanged: () {
- controller.pause();
- controller.gotoFrame(i.toInt());
- },
- aiDiagnosticOrganName: _buildAIDiagnosticOrgans(),
- curCursorIndex: curCursorIndex,
- );
- }).toList(),
- _CursorTrack(
- constraints: constraints,
- cursorUpdateCallback: (localPositionX) {
- setState(() {
- curCursorIndex = (localPositionX / constraints.maxWidth * max)
- .roundToDouble();
- });
- },
- cursorExit: () {
- setState(() {
- curCursorIndex = -10;
- });
- },
- )
- ],
- );
- }),
- );
- }
- void onControllerEvent(Object sender, VidPlayerEvent e) {
- if (e is VidPlayerFrameIndexChangeEvent) {
- onPlayFrameIndexChanged(e);
- }
- }
- void onPlayFrameIndexChanged(VidPlayerFrameIndexChangeEvent e) {
- if (mounted) {
- setState(() {});
- }
- }
- String _buildAIDiagnosticOrgans() {
- switch (diagnosisOrgan) {
- case DiagnosisOrganEnum.Breast:
- return i18nBook.remedical.breast.t;
- case DiagnosisOrganEnum.Abdomen:
- return i18nBook.bodyParts.abdomen.t;
- case DiagnosisOrganEnum.Liver:
- return i18nBook.remedical.liver.t;
- case DiagnosisOrganEnum.Cholecyst:
- return i18nBook.remedical.cholecyst.t;
- case DiagnosisOrganEnum.Kidney:
- return i18nBook.remedical.kidney.t;
- case DiagnosisOrganEnum.Spleen:
- return i18nBook.remedical.spleen.t;
- default:
- return '';
- }
- }
- }
- // https://juejin.cn/post/6959703051586240549
- class _FullWidthTrackShape extends RoundedRectSliderTrackShape {
- const _FullWidthTrackShape();
- @override
- Rect getPreferredRect({
- required RenderBox parentBox,
- Offset offset = Offset.zero,
- required SliderThemeData sliderTheme,
- bool isEnabled = false,
- bool isDiscrete = false,
- }) {
- final double trackHeight = sliderTheme.trackHeight ?? 2;
- final double trackLeft = offset.dx;
- final double trackTop =
- offset.dy + (parentBox.size.height - trackHeight) / 2;
- // 让轨道宽度等于 Slider 宽度
- final double trackWidth = parentBox.size.width;
- return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
- }
- }
- class MeasureTooltip extends StatelessWidget {
- final String tooltips;
- final FWidget child;
- const MeasureTooltip({
- Key? key,
- required this.tooltips,
- required this.child,
- }) : super(key: key);
- @override
- Widget build(BuildContext context) {
- return FMaterialTooltip(
- message: tooltips,
- preferBelow: false,
- padding: const EdgeInsets.all(8),
- textStyle: const TextStyle(
- fontSize: 16,
- color: Colors.white,
- ),
- triggerMode: TooltipTriggerMode.tap, // 单击 显示Tooltip
- verticalOffset: 30,
- child: child,
- );
- }
- }
- /// AI节点
- class _PanelPoint extends StatefulWidget {
- const _PanelPoint({
- required this.max,
- required this.index,
- required this.constraints,
- required this.currentIndex,
- required this.onChanged,
- required this.curCursorIndex,
- required this.aiDiagnosticOrganName,
- });
- final double max;
- final double index;
- final BoxConstraints constraints;
- final double currentIndex;
- final VoidCallback onChanged;
- final double curCursorIndex;
- final String aiDiagnosticOrganName;
- @override
- State<_PanelPoint> createState() => _PanelPointState();
- }
- class _PanelPointState extends State<_PanelPoint> {
- FWidget marker({
- required double size,
- required Color color,
- }) {
- return FInkWell(
- onTap: widget.onChanged,
- child: FContainer(
- height: size,
- width: size,
- decoration: BoxDecoration(
- color: color,
- borderRadius: BorderRadius.circular(size / 2),
- ),
- ),
- );
- }
- @override
- Widget build(BuildContext context) {
- double size =
- max(8 - (widget.curCursorIndex - widget.currentIndex).abs(), 3);
- return Positioned(
- left: widget.currentIndex / widget.max * widget.constraints.maxWidth -
- (size / 2),
- top: widget.constraints.maxHeight / 2 + 15 + size,
- child: MeasureTooltip(
- tooltips: widget.aiDiagnosticOrganName,
- child: marker(
- size: size,
- color: widget.currentIndex == widget.index
- ? const Color.fromARGB(255, 255, 111, 45)
- : const Color.fromARGB(255, 252, 255, 45),
- ),
- ),
- );
- }
- }
- class _CursorTrack extends StatelessWidget {
- const _CursorTrack({
- required this.constraints,
- required this.cursorUpdateCallback,
- required this.cursorExit,
- });
- final BoxConstraints constraints;
- final ValueCallback<double> cursorUpdateCallback;
- final VoidCallback cursorExit;
- @override
- Widget build(BuildContext context) {
- return Positioned(
- top: constraints.maxHeight / 2 + 10,
- child: MouseRegion(
- onHover: (e) {
- cursorUpdateCallback(e.localPosition.dx);
- },
- onExit: (e) => {cursorExit()},
- opaque: false,
- child: SizedBox(
- width: constraints.maxWidth,
- height: 40,
- child: Container(),
- )),
- );
- }
- }
|