measure_page_test.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. import 'package:fis_jsonrpc/rpc.dart';
  2. import 'package:fis_measure/interfaces/enums/annotation.dart';
  3. import 'package:fis_measure/interfaces/process/items/measure_terms.dart';
  4. import 'package:fis_measure/interfaces/process/player/play_controller.dart';
  5. import 'package:fis_measure/interfaces/process/workspace/application.dart';
  6. import 'package:fis_measure/interfaces/process/workspace/exam_info.dart';
  7. import 'package:fis_measure/interfaces/process/workspace/measure_controller.dart';
  8. import 'package:fis_measure/process/workspace/measure_controller.dart';
  9. import 'package:fis_measure/process/workspace/measure_data_controller.dart';
  10. import 'package:fis_measure/view/main/desktop.dart';
  11. import 'package:flutter/material.dart';
  12. import 'package:get/get.dart';
  13. class MeasureDataTester {
  14. static Future<List<RemedicalItemList>> getRemedicalList(
  15. String patientCode,
  16. String recordCode,
  17. String token,
  18. ) async {
  19. return [];
  20. }
  21. static Future<RemedicalInfoDTO?> getImageInfo(
  22. String remedicalCode,
  23. String token,
  24. ) async {
  25. return null;
  26. }
  27. static Future<MeasureApplicationDTO?> getMeasureApplication(
  28. dynamic args) async {
  29. return null;
  30. }
  31. }
  32. class MeasureTestPage extends StatefulWidget {
  33. const MeasureTestPage({Key? key}) : super(key: key);
  34. @override
  35. State<StatefulWidget> createState() => _MeasureTestPageState();
  36. }
  37. class _MeasureTestPageState extends State<MeasureTestPage> {
  38. static const C_LINEAR_TISSUE =
  39. // "http://43.138.119.65:9303/Flyinsono-BJ-1300984704.VCS.AP-BeiJing/9eb581250c6845b7800f0ba00218e043.VID";
  40. // "http://cdn-bj.fis.plus/0B344F48BA574ECD82B7FEDB8848421A.vid";//单幅TM
  41. // "http://cdn-bj.fis.plus/6B6E069659D14E7299EB9F6EFCDE9C8C.vid"; //双幅单TissueConvex
  42. // "http://cdn-bj.fis.plus/062643B82365437DB95F3811580AF3ED.vid"; //四幅单模式
  43. // "http://cdn-bj.fis.plus/EA90D146049D416E8E466B7446E00001.vid"; //四幅Doppler
  44. "http://cdn-bj.fis.plus/3rd_linearTvTissue2.vid"; //魔盒
  45. // "http://cdn-bj.fis.plus/81FFF8E5E078473FA687FBE81C4869B1.vid"; // 魔盒TV
  46. static const C_CONVEX_TISSUE =
  47. "http://cdn-bj.fis.plus/FEB1AAE5D9C24839BEE31B16E8CB450A.vid";
  48. final datac = Get.put(MeasureDataController(
  49. MeasureDataTester.getRemedicalList,
  50. MeasureDataTester.getImageInfo,
  51. MeasureDataTester.getMeasureApplication,
  52. ));
  53. final controller = Get.put<IMeasureController>(MeasureController(
  54. "12345",
  55. imagesFetchFunc: (code) async {
  56. return <ExamImageInfo>[
  57. ExamImageInfo(C_LINEAR_TISSUE, C_LINEAR_TISSUE),
  58. ExamImageInfo(C_CONVEX_TISSUE, C_CONVEX_TISSUE)
  59. ];
  60. },
  61. ));
  62. bool loaded = false;
  63. int opType = 0;
  64. bool useArrowAnnotation = false;
  65. @override
  66. void initState() {
  67. controller.load().then((value) {
  68. // 加载指定图像
  69. controller.examInfo.selectedImageIndex = 0;
  70. });
  71. controller.imageLoaded.addListener(onImageLoaded);
  72. super.initState();
  73. }
  74. @override
  75. void dispose() {
  76. controller.imageLoaded.removeListener(onImageLoaded);
  77. controller.dispose();
  78. Get.delete<IMeasureController>();
  79. super.dispose();
  80. }
  81. void onImageLoaded(Object sender, ExamImageInfo? e) {
  82. if (!mounted) return;
  83. if (e != null) {
  84. setState(() {
  85. loaded = true;
  86. });
  87. }
  88. }
  89. @override
  90. Widget build(BuildContext context) {
  91. Widget body;
  92. if (!loaded) {
  93. const loadingWidget = Center(child: CircularProgressIndicator());
  94. body = Row(
  95. children: const [
  96. SizedBox(
  97. width: 300,
  98. child: loadingWidget,
  99. ),
  100. VerticalDivider(),
  101. Expanded(child: loadingWidget),
  102. ],
  103. );
  104. } else {
  105. body = Row(
  106. key: ValueKey(controller.examInfo.selectedImageIndex),
  107. children: [
  108. opType == 1
  109. ? const _MeasureLeftAnnotation()
  110. : const _MeasureLeftBoard(),
  111. const VerticalDivider(),
  112. const Expanded(
  113. child: MeasureRightBoard(),
  114. ),
  115. ],
  116. );
  117. }
  118. return Scaffold(
  119. backgroundColor: const Color.fromARGB(255, 53, 55, 51),
  120. appBar: AppBar(
  121. actions: [
  122. TextButton.icon(
  123. onPressed: () {
  124. setState(() {
  125. if (useArrowAnnotation) {
  126. useArrowAnnotation = false;
  127. controller.workingApplication.switchAnnotation();
  128. } else {
  129. useArrowAnnotation = true;
  130. controller.workingApplication
  131. .switchAnnotation(AnnotationType.arrow);
  132. }
  133. });
  134. },
  135. icon: const Icon(Icons.arrow_right_alt_sharp),
  136. label: Text(
  137. "箭头",
  138. style: TextStyle(
  139. color: useArrowAnnotation ? Colors.amber : Colors.white,
  140. ),
  141. ),
  142. ),
  143. TextButton(
  144. onPressed: () {
  145. setState(() {
  146. opType = opType == 1 ? 0 : 1;
  147. });
  148. },
  149. child: Text(
  150. opType == 1 ? "注释" : "测量",
  151. style: const TextStyle(
  152. color: Colors.white,
  153. ),
  154. ),
  155. ),
  156. TextButton(
  157. onPressed: () {
  158. if (controller.examInfo.selectedImageIndex == 0) return;
  159. controller.examInfo.selectedImageIndex = 0;
  160. },
  161. child: Text(
  162. '线阵',
  163. style: TextStyle(
  164. color: controller.examInfo.selectedImageIndex == 0
  165. ? Colors.amber
  166. : Colors.white,
  167. ),
  168. ),
  169. ),
  170. TextButton(
  171. onPressed: () {
  172. if (controller.examInfo.selectedImageIndex == 1) return;
  173. controller.examInfo.selectedImageIndex = 1;
  174. },
  175. child: Text(
  176. '扇阵',
  177. style: TextStyle(
  178. color: controller.examInfo.selectedImageIndex == 1
  179. ? Colors.amber
  180. : Colors.white,
  181. ),
  182. ),
  183. ),
  184. ],
  185. leading: IconButton(
  186. onPressed: () {
  187. Navigator.of(context).pop();
  188. },
  189. icon: const Icon(Icons.arrow_back),
  190. ),
  191. ),
  192. body: body,
  193. );
  194. }
  195. }
  196. class MeasureRightBoard extends StatefulWidget {
  197. const MeasureRightBoard({Key? key}) : super(key: key);
  198. @override
  199. State<StatefulWidget> createState() => _MeasureRightBoardState();
  200. }
  201. class _MeasureRightBoardState extends State<MeasureRightBoard> {
  202. final playerController = Get.find<IPlayerController>();
  203. @override
  204. Widget build(BuildContext context) {
  205. return Container(
  206. padding: const EdgeInsets.all(8).copyWith(left: 0),
  207. child: const MeasureMainView(),
  208. );
  209. }
  210. }
  211. class _MeasureLeftBoard extends StatefulWidget {
  212. const _MeasureLeftBoard({Key? key}) : super(key: key);
  213. @override
  214. State<StatefulWidget> createState() => _MeasureLeftBoardState();
  215. }
  216. class _MeasureLeftBoardState extends State<_MeasureLeftBoard> {
  217. // ignore: non_constant_identifier_names
  218. static final C_SUPPORTED_ITEMS = <String>[
  219. MeasureTerms.Distance,
  220. MeasureTerms.Perimeter,
  221. MeasureTerms.Area,
  222. MeasureTerms.Depth,
  223. ];
  224. final scrollController = ScrollController();
  225. final application = Get.find<IApplication>();
  226. int activeIndex = 0;
  227. @override
  228. void initState() {
  229. application.switchItemByName(C_SUPPORTED_ITEMS[0]);
  230. application.canMeasureChanged.addListener(_onCanMeasureChanged);
  231. super.initState();
  232. }
  233. @override
  234. dispose() {
  235. application.canMeasureChanged.removeListener(_onCanMeasureChanged);
  236. super.dispose();
  237. }
  238. _onCanMeasureChanged(Object sender, bool e) {
  239. if (e && mounted) {
  240. changeItem(0);
  241. }
  242. }
  243. @override
  244. Widget build(BuildContext context) {
  245. return Container(
  246. width: 300,
  247. padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12),
  248. child: Scrollbar(
  249. controller: scrollController,
  250. isAlwaysShown: true,
  251. child: ListView.separated(
  252. controller: scrollController,
  253. itemCount: C_SUPPORTED_ITEMS.length,
  254. itemBuilder: (BuildContext context, int index) {
  255. final name = C_SUPPORTED_ITEMS[index];
  256. final active = index == activeIndex;
  257. return active
  258. ? ElevatedButton(
  259. onPressed: () => changeItem(index),
  260. child: Text(name),
  261. style: ElevatedButton.styleFrom(
  262. fixedSize: const Size.fromHeight(50),
  263. ),
  264. )
  265. : OutlinedButton(
  266. onPressed: () => changeItem(index),
  267. child: Text(name),
  268. style: OutlinedButton.styleFrom(
  269. fixedSize: const Size.fromHeight(50),
  270. ),
  271. );
  272. },
  273. separatorBuilder: (BuildContext context, int index) {
  274. return const SizedBox(height: 8);
  275. },
  276. ),
  277. ),
  278. );
  279. }
  280. void changeItem(int index) {
  281. setState(() {
  282. activeIndex = index;
  283. });
  284. final name = C_SUPPORTED_ITEMS[index];
  285. application.switchItemByName(name);
  286. }
  287. }
  288. class _MeasureLeftAnnotation extends StatefulWidget {
  289. const _MeasureLeftAnnotation({Key? key}) : super(key: key);
  290. @override
  291. State<StatefulWidget> createState() => _MeasureLeftAnnotationState();
  292. }
  293. class _MeasureLeftAnnotationState extends State<_MeasureLeftAnnotation> {
  294. // ignore: non_constant_identifier_names
  295. static final C_SUPPORTED_TEXTS = <String>[
  296. "肝左叶",
  297. "胆囊",
  298. "脾脏",
  299. "结石",
  300. "积液",
  301. ];
  302. final scrollController = ScrollController();
  303. final application = Get.find<IApplication>();
  304. @override
  305. void initState() {
  306. // application.switchAnnotation(AnnotationType.label, C_SUPPORTED_TEXTS[0]);
  307. // application.switchAnnotation(AnnotationType.arrow);
  308. application.switchAnnotation(AnnotationType.input);
  309. super.initState();
  310. }
  311. @override
  312. Widget build(BuildContext context) {
  313. return Container(
  314. width: 300,
  315. padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12),
  316. child: Scrollbar(
  317. controller: scrollController,
  318. isAlwaysShown: true,
  319. child: ListView.separated(
  320. controller: scrollController,
  321. itemCount: C_SUPPORTED_TEXTS.length,
  322. itemBuilder: (BuildContext context, int index) {
  323. final name = C_SUPPORTED_TEXTS[index];
  324. const style = TextStyle(color: Colors.white, fontSize: 16);
  325. const dragStyle = TextStyle(color: Colors.amber, fontSize: 18);
  326. // TODO: melon - set drag cursor after version updated up then 3.0
  327. // https://github.com/flutter/flutter/pull/100475
  328. return Draggable<String>(
  329. data: name,
  330. dragAnchorStrategy: (data, context, offset) {
  331. // return offset - Offset(120, 14);
  332. return Offset.zero;
  333. },
  334. child: OutlinedButton(
  335. child: Text(name, style: style),
  336. onPressed: () {
  337. application.switchAnnotation(AnnotationType.label, name);
  338. },
  339. style: OutlinedButton.styleFrom(
  340. shape: RoundedRectangleBorder(
  341. borderRadius: BorderRadius.circular(4),
  342. ),
  343. side: BorderSide(color: Colors.grey.shade100),
  344. fixedSize: const Size.fromHeight(44),
  345. ),
  346. ),
  347. feedback: Material(
  348. color: Colors.transparent,
  349. child: Text(name, style: dragStyle),
  350. ),
  351. onDragStarted: () {
  352. application.switchAnnotation(AnnotationType.label, name);
  353. },
  354. );
  355. },
  356. separatorBuilder: (BuildContext context, int index) {
  357. return const SizedBox(height: 8);
  358. },
  359. ),
  360. ),
  361. );
  362. }
  363. }