commit b2842e622ca13a9763188a04e46f99fcce6c59fb Author: xueyan hu <576627988@qq.com> Date: Mon Jan 24 10:10:45 2022 +0800 first commit diff --git a/src/Combinear.qss b/src/Combinear.qss new file mode 100644 index 0000000..6b592e2 --- /dev/null +++ b/src/Combinear.qss @@ -0,0 +1,157 @@ +/*-----QWidget-----*/ +*{ + font:12px; +} +QToolBar +{ + background-color: #323232; + border-bottom:1px solid #cdcdcd; +} + +QWidget#statusBar +{ + background-color: #323232; + border:1px solid #5a5a5a; + color:white; +} + +/*-----QToolButton-----*/ +QToolButton +{ + border-radius: 5px; margin:0px 0px 0px 0px; padding:3px; +} + +QToolButton::menu-button +{ + background-color: transparent; +} + +QToolButton::menu-arrow { + image: url(:/InfiniteViewer/Icon/triangle-down-white.png); + width:6px; +} + +QToolButton:hover +{ + background-color: #8a8a8a; +} +QToolButton:checked +{ + background-color: #646464; +} +QToolButton:pressed +{ + background-color: #646464; +} + +/*-----QMenu-----*/ +QMenu { + background-color: #f0f0f0; +} + + +QMenu::item { + background-color: transparent; +} + +QMenu::item:selected { /* when user selects item using mouse or keyboard */ + background-color: #8a8a8a; +} + +/*-----DicomImageView-----*/ +DicomImageView#dicomview +{ + border: 1px solid #646464; +} +QFrame#wrapper +{ + border-top: 1px solid #646464; + border-bottom: 0px; + border-left: 0px; + border-right: 0px; +} + + + +/*-----QScrollBar-----*/ +QScrollBar:vertical { + border: 0px solid grey; + background: #2d2d2d; + width: 15px; + margin: 0px 0 0px 0; + } + + QScrollBar::handle:vertical { + background: #5a5a5a; + min-height: 25px; + } + + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: #2d2d2d; + } + + /*-----QSlider-----*/ +QSlider::groove:horizontal +{ + background-color: transparent; + height: 3px; + +} + + +QSlider::sub-page:horizontal +{ + background-color: #131313; + +} + + +QSlider::add-page:horizontal +{ + background-color: #131313; + +} + + +QSlider::handle:horizontal +{ + background-color: #dd8c98; + width: 14px; + margin-top: -6px; + margin-bottom: -6px; + border-radius: 6px; + +} + + +QSlider::handle:horizontal:hover +{ + background-color: #d89e25; + border-radius: 6px; + +} + + +QSlider::sub-page:horizontal:disabled +{ + background-color: #bbb; + border-color: #999; + +} + + +QSlider::add-page:horizontal:disabled +{ + background-color: #eee; + border-color: #999; + +} + + +QSlider::handle:horizontal:disabled +{ + background-color: #eee; + border: 1px solid #aaa; + border-radius: 3px; + +} \ No newline at end of file diff --git a/src/Icon/angle.png b/src/Icon/angle.png new file mode 100644 index 0000000..2935f4c Binary files /dev/null and b/src/Icon/angle.png differ diff --git a/src/Icon/anno.png b/src/Icon/anno.png new file mode 100644 index 0000000..90edd64 Binary files /dev/null and b/src/Icon/anno.png differ diff --git a/src/Icon/arrow.png b/src/Icon/arrow.png new file mode 100644 index 0000000..d5819b6 Binary files /dev/null and b/src/Icon/arrow.png differ diff --git a/src/Icon/cine.png b/src/Icon/cine.png new file mode 100644 index 0000000..82c6755 Binary files /dev/null and b/src/Icon/cine.png differ diff --git a/src/Icon/close.png b/src/Icon/close.png new file mode 100644 index 0000000..cc3dbba Binary files /dev/null and b/src/Icon/close.png differ diff --git a/src/Icon/cursor.png b/src/Icon/cursor.png new file mode 100644 index 0000000..6e71ae6 Binary files /dev/null and b/src/Icon/cursor.png differ diff --git a/src/Icon/distance.png b/src/Icon/distance.png new file mode 100644 index 0000000..5014b70 Binary files /dev/null and b/src/Icon/distance.png differ diff --git a/src/Icon/ellipse.png b/src/Icon/ellipse.png new file mode 100644 index 0000000..f43bfc4 Binary files /dev/null and b/src/Icon/ellipse.png differ diff --git a/src/Icon/flip.png b/src/Icon/flip.png new file mode 100644 index 0000000..75e34d0 Binary files /dev/null and b/src/Icon/flip.png differ diff --git a/src/Icon/full_screen.png b/src/Icon/full_screen.png new file mode 100644 index 0000000..c5a7295 Binary files /dev/null and b/src/Icon/full_screen.png differ diff --git a/src/Icon/fusion.png b/src/Icon/fusion.png new file mode 100644 index 0000000..6c74cfc Binary files /dev/null and b/src/Icon/fusion.png differ diff --git a/src/Icon/grid.png b/src/Icon/grid.png new file mode 100644 index 0000000..170f4d3 Binary files /dev/null and b/src/Icon/grid.png differ diff --git a/src/Icon/hidden.png b/src/Icon/hidden.png new file mode 100644 index 0000000..a98732c Binary files /dev/null and b/src/Icon/hidden.png differ diff --git a/src/Icon/image.png b/src/Icon/image.png new file mode 100644 index 0000000..b586edb Binary files /dev/null and b/src/Icon/image.png differ diff --git a/src/Icon/import.png b/src/Icon/import.png new file mode 100644 index 0000000..7b10606 Binary files /dev/null and b/src/Icon/import.png differ diff --git a/src/Icon/import2.png b/src/Icon/import2.png new file mode 100644 index 0000000..4400c51 Binary files /dev/null and b/src/Icon/import2.png differ diff --git a/src/Icon/line.png b/src/Icon/line.png new file mode 100644 index 0000000..126dffe Binary files /dev/null and b/src/Icon/line.png differ diff --git a/src/Icon/logo.png b/src/Icon/logo.png new file mode 100644 index 0000000..72e06ec Binary files /dev/null and b/src/Icon/logo.png differ diff --git a/src/Icon/max.png b/src/Icon/max.png new file mode 100644 index 0000000..ae9ace8 Binary files /dev/null and b/src/Icon/max.png differ diff --git a/src/Icon/maximize-restore.png b/src/Icon/maximize-restore.png new file mode 100644 index 0000000..7862a3e Binary files /dev/null and b/src/Icon/maximize-restore.png differ diff --git a/src/Icon/minimize.png b/src/Icon/minimize.png new file mode 100644 index 0000000..cceb202 Binary files /dev/null and b/src/Icon/minimize.png differ diff --git a/src/Icon/openfile.png b/src/Icon/openfile.png new file mode 100644 index 0000000..7f35fc4 Binary files /dev/null and b/src/Icon/openfile.png differ diff --git a/src/Icon/pan.png b/src/Icon/pan.png new file mode 100644 index 0000000..e12fd97 Binary files /dev/null and b/src/Icon/pan.png differ diff --git a/src/Icon/polygon.png b/src/Icon/polygon.png new file mode 100644 index 0000000..43c8d74 Binary files /dev/null and b/src/Icon/polygon.png differ diff --git a/src/Icon/polyline.png b/src/Icon/polyline.png new file mode 100644 index 0000000..e22d2fc Binary files /dev/null and b/src/Icon/polyline.png differ diff --git a/src/Icon/pq/pqApply.png b/src/Icon/pq/pqApply.png new file mode 100644 index 0000000..26fe501 Binary files /dev/null and b/src/Icon/pq/pqApply.png differ diff --git a/src/Icon/pq/pqBold24.png b/src/Icon/pq/pqBold24.png new file mode 100644 index 0000000..4d2fa07 Binary files /dev/null and b/src/Icon/pq/pqBold24.png differ diff --git a/src/Icon/pq/pqCancel.png b/src/Icon/pq/pqCancel.png new file mode 100644 index 0000000..ce7e425 Binary files /dev/null and b/src/Icon/pq/pqCancel.png differ diff --git a/src/Icon/pq/pqDelete.png b/src/Icon/pq/pqDelete.png new file mode 100644 index 0000000..550d57d Binary files /dev/null and b/src/Icon/pq/pqDelete.png differ diff --git a/src/Icon/pq/pqItalics24.png b/src/Icon/pq/pqItalics24.png new file mode 100644 index 0000000..173d79e Binary files /dev/null and b/src/Icon/pq/pqItalics24.png differ diff --git a/src/Icon/pq/pqShadow24.png b/src/Icon/pq/pqShadow24.png new file mode 100644 index 0000000..dc9259c Binary files /dev/null and b/src/Icon/pq/pqShadow24.png differ diff --git a/src/Icon/pq/pqVcrBack.png b/src/Icon/pq/pqVcrBack.png new file mode 100644 index 0000000..46c0e3a Binary files /dev/null and b/src/Icon/pq/pqVcrBack.png differ diff --git a/src/Icon/pq/pqVcrFirst.png b/src/Icon/pq/pqVcrFirst.png new file mode 100644 index 0000000..1a5e0de Binary files /dev/null and b/src/Icon/pq/pqVcrFirst.png differ diff --git a/src/Icon/pq/pqVcrForward.png b/src/Icon/pq/pqVcrForward.png new file mode 100644 index 0000000..513adc0 Binary files /dev/null and b/src/Icon/pq/pqVcrForward.png differ diff --git a/src/Icon/pq/pqVcrFpsDown.png b/src/Icon/pq/pqVcrFpsDown.png new file mode 100644 index 0000000..21586dd Binary files /dev/null and b/src/Icon/pq/pqVcrFpsDown.png differ diff --git a/src/Icon/pq/pqVcrFpsUp.png b/src/Icon/pq/pqVcrFpsUp.png new file mode 100644 index 0000000..61a4030 Binary files /dev/null and b/src/Icon/pq/pqVcrFpsUp.png differ diff --git a/src/Icon/pq/pqVcrLast.png b/src/Icon/pq/pqVcrLast.png new file mode 100644 index 0000000..4d49b13 Binary files /dev/null and b/src/Icon/pq/pqVcrLast.png differ diff --git a/src/Icon/pq/pqVcrPause.png b/src/Icon/pq/pqVcrPause.png new file mode 100644 index 0000000..94e3066 Binary files /dev/null and b/src/Icon/pq/pqVcrPause.png differ diff --git a/src/Icon/pq/pqVcrPlay.png b/src/Icon/pq/pqVcrPlay.png new file mode 100644 index 0000000..4dc9808 Binary files /dev/null and b/src/Icon/pq/pqVcrPlay.png differ diff --git a/src/Icon/save.png b/src/Icon/save.png new file mode 100644 index 0000000..9982e5b Binary files /dev/null and b/src/Icon/save.png differ diff --git a/src/Icon/slice.png b/src/Icon/slice.png new file mode 100644 index 0000000..8990034 Binary files /dev/null and b/src/Icon/slice.png differ diff --git a/src/Icon/sync/sync_auto.png b/src/Icon/sync/sync_auto.png new file mode 100644 index 0000000..c0ebc17 Binary files /dev/null and b/src/Icon/sync/sync_auto.png differ diff --git a/src/Icon/sync/sync_dis.png b/src/Icon/sync/sync_dis.png new file mode 100644 index 0000000..0565765 Binary files /dev/null and b/src/Icon/sync/sync_dis.png differ diff --git a/src/Icon/sync/sync_manual.png b/src/Icon/sync/sync_manual.png new file mode 100644 index 0000000..c579a6b Binary files /dev/null and b/src/Icon/sync/sync_manual.png differ diff --git a/src/Icon/text.png b/src/Icon/text.png new file mode 100644 index 0000000..c6aa7d6 Binary files /dev/null and b/src/Icon/text.png differ diff --git a/src/Icon/textProp.png b/src/Icon/textProp.png new file mode 100644 index 0000000..04ccacf Binary files /dev/null and b/src/Icon/textProp.png differ diff --git a/src/Icon/trashbin.png b/src/Icon/trashbin.png new file mode 100644 index 0000000..e2b3735 Binary files /dev/null and b/src/Icon/trashbin.png differ diff --git a/src/Icon/triangle-down-black.png b/src/Icon/triangle-down-black.png new file mode 100644 index 0000000..5a63866 Binary files /dev/null and b/src/Icon/triangle-down-black.png differ diff --git a/src/Icon/triangle-down-white.png b/src/Icon/triangle-down-white.png new file mode 100644 index 0000000..0cd426e Binary files /dev/null and b/src/Icon/triangle-down-white.png differ diff --git a/src/Icon/triangle.png b/src/Icon/triangle.png new file mode 100644 index 0000000..4cacee9 Binary files /dev/null and b/src/Icon/triangle.png differ diff --git a/src/Icon/windowlevel.png b/src/Icon/windowlevel.png new file mode 100644 index 0000000..79d6082 Binary files /dev/null and b/src/Icon/windowlevel.png differ diff --git a/src/Icon/zoom.png b/src/Icon/zoom.png new file mode 100644 index 0000000..9285704 Binary files /dev/null and b/src/Icon/zoom.png differ diff --git a/src/QDicomViewer.qrc b/src/QDicomViewer.qrc new file mode 100644 index 0000000..cbe3513 --- /dev/null +++ b/src/QDicomViewer.qrc @@ -0,0 +1,67 @@ + + + Resources/import/add.png + Resources/import/arrow.png + Resources/import/close.png + Resources/import/icon.png + Resources/import/max.png + Resources/import/max_ba.png + Resources/import/min.png + Resources/import/remove.png + Resources/import/setting.png + + + Combinear.qss + + + Icon/angle.png + Icon/distance.png + Icon/grid.png + Icon/hidden.png + Icon/openfile.png + Icon/pan.png + Icon/windowlevel.png + Icon/zoom.png + Icon/sync/sync_auto.png + Icon/sync/sync_dis.png + Icon/sync/sync_manual.png + Icon/save.png + Icon/anno.png + Icon/flip.png + Icon/cine.png + Icon/fusion.png + Icon/trashbin.png + Icon/slice.png + Icon/import.png + Icon/logo.png + Icon/close.png + Icon/max.png + Icon/triangle-down-black.png + Icon/triangle-down-white.png + Icon/text.png + Icon/ellipse.png + Icon/textProp.png + Icon/arrow.png + Icon/polygon.png + Icon/polyline.png + Icon/full_screen.png + Icon/maximize-restore.png + Icon/minimize.png + + + Icon/pq/pqBold24.png + Icon/pq/pqItalics24.png + Icon/pq/pqShadow24.png + Icon/pq/pqApply.png + Icon/pq/pqCancel.png + Icon/pq/pqDelete.png + Icon/pq/pqVcrBack.png + Icon/pq/pqVcrFirst.png + Icon/pq/pqVcrForward.png + Icon/pq/pqVcrFpsDown.png + Icon/pq/pqVcrFpsUp.png + Icon/pq/pqVcrLast.png + Icon/pq/pqVcrPause.png + Icon/pq/pqVcrPlay.png + + diff --git a/src/Resources/import/add.png b/src/Resources/import/add.png new file mode 100644 index 0000000..fdaa55d Binary files /dev/null and b/src/Resources/import/add.png differ diff --git a/src/Resources/import/arrow.png b/src/Resources/import/arrow.png new file mode 100644 index 0000000..f787a36 Binary files /dev/null and b/src/Resources/import/arrow.png differ diff --git a/src/Resources/import/close.png b/src/Resources/import/close.png new file mode 100644 index 0000000..9d56fb3 Binary files /dev/null and b/src/Resources/import/close.png differ diff --git a/src/Resources/import/icon.png b/src/Resources/import/icon.png new file mode 100644 index 0000000..9f5c1fa Binary files /dev/null and b/src/Resources/import/icon.png differ diff --git a/src/Resources/import/max.png b/src/Resources/import/max.png new file mode 100644 index 0000000..8bd4a0c Binary files /dev/null and b/src/Resources/import/max.png differ diff --git a/src/Resources/import/max_ba.png b/src/Resources/import/max_ba.png new file mode 100644 index 0000000..cc66395 Binary files /dev/null and b/src/Resources/import/max_ba.png differ diff --git a/src/Resources/import/min.png b/src/Resources/import/min.png new file mode 100644 index 0000000..165d575 Binary files /dev/null and b/src/Resources/import/min.png differ diff --git a/src/Resources/import/radius_back.png b/src/Resources/import/radius_back.png new file mode 100644 index 0000000..fdd532b Binary files /dev/null and b/src/Resources/import/radius_back.png differ diff --git a/src/Resources/import/radius_front.png b/src/Resources/import/radius_front.png new file mode 100644 index 0000000..a7e8ecc Binary files /dev/null and b/src/Resources/import/radius_front.png differ diff --git a/src/Resources/import/remove.png b/src/Resources/import/remove.png new file mode 100644 index 0000000..801a58d Binary files /dev/null and b/src/Resources/import/remove.png differ diff --git a/src/Resources/import/setting.png b/src/Resources/import/setting.png new file mode 100644 index 0000000..45ac571 Binary files /dev/null and b/src/Resources/import/setting.png differ diff --git a/src/form/QDicomViewer.ui b/src/form/QDicomViewer.ui new file mode 100644 index 0000000..4246582 --- /dev/null +++ b/src/form/QDicomViewer.ui @@ -0,0 +1,166 @@ + + + QDicomViewerClass + + + + 0 + 0 + 1099 + 761 + + + + QDicomViewer + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + + Britannic Bold + + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + true + + + + + toolBar + + + false + + + false + + + TopToolBarArea + + + false + + + + + + + ViewContainerWidget + QWidget +
view/viewcontainerwidget.h
+ 1 +
+ + ThumbnailBarWidget + QWidget +
view/thumbnailbarwidget.h
+ 1 +
+
+ + + + + +
diff --git a/src/form/calibrationWidget.ui b/src/form/calibrationWidget.ui new file mode 100644 index 0000000..3dc0e9a --- /dev/null +++ b/src/form/calibrationWidget.ui @@ -0,0 +1,143 @@ + + + calibrationWidget + + + + 0 + 0 + 321 + 96 + + + + + Arial + + + + Length Calibration + + + + + + + + + + Arial + + + + Enter new distance + + + + + + + + + + mm + + + + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 131 + 31 + + + + + + + + + Arial + + + + OK + + + + + + + + Arial + + + + Cancel + + + + + + + + + + + okButton + clicked() + calibrationWidget + accept() + + + 278 + 253 + + + 96 + 254 + + + + + cancelButton + clicked() + calibrationWidget + reject() + + + 369 + 253 + + + 179 + 282 + + + + + diff --git a/src/form/customwindow.ui b/src/form/customwindow.ui new file mode 100644 index 0000000..591a0f5 --- /dev/null +++ b/src/form/customwindow.ui @@ -0,0 +1,154 @@ + + + Customwindow + + + Qt::WindowModal + + + + 0 + 0 + 345 + 185 + + + + + 0 + 0 + + + + + 345 + 185 + + + + Customwindow + + + + + + + + + + + + Window level + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 71 + 20 + + + + + + + + + 70 + 25 + + + + + + + + + + + + + + Window width + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 71 + 20 + + + + + + + + + 70 + 25 + + + + + + + + + + + + + + Qt::Horizontal + + + + 126 + 20 + + + + + + + + OK + + + + + + + Cancel + + + + + + + + + + + + + + + diff --git a/src/form/exportdialog.ui b/src/form/exportdialog.ui new file mode 100644 index 0000000..35897e8 --- /dev/null +++ b/src/form/exportdialog.ui @@ -0,0 +1,294 @@ + + + ExportDialog + + + + 0 + 0 + 544 + 384 + + + + + 544 + 384 + + + + Dialog + + + + + + Export Type + + + + + + Export File + + + + + + + Current Image + + + true + + + buttonGroup + + + + + + + Current Series + + + buttonGroup + + + + + + + All Opened Series + + + buttonGroup + + + + + + + File Format + + + + + + + JPEG + + + true + + + buttonGroup_2 + + + + + + + BMP + + + buttonGroup_2 + + + + + + + PNG + + + buttonGroup_2 + + + + + + + TIFF + + + buttonGroup_2 + + + + + + + DICOM + + + buttonGroup_2 + + + + + + + + + + Export Location + + + + + + Export Folder + + + + + + + + + + Qt::NoFocus + + + Choose Folder... + + + + + + + FileName Prefix + + + + + + + + + + Qt::Horizontal + + + + 199 + 20 + + + + + + + + + + + File Settings + + + + + + Annotations + + + + + + + Full + + + true + + + buttonGroup_3 + + + + + + + + 0 + 0 + + + + Basic(anonymous) + + + buttonGroup_3 + + + + + + + Disabled + + + buttonGroup_3 + + + + + + + + + + 0 + + + true + + + QProgressBar::TopToBottom + + + + + + + + + + Qt::Horizontal + + + + 343 + 20 + + + + + + + + Export + + + + + + + Close + + + + + + + + + + + + + + + + diff --git a/src/form/gridpopwidget.ui b/src/form/gridpopwidget.ui new file mode 100644 index 0000000..bf80f78 --- /dev/null +++ b/src/form/gridpopwidget.ui @@ -0,0 +1,2366 @@ + + + GridPopWidget + + + + 0 + 0 + 278 + 224 + + + + + + + + + 255 + 255 + 255 + + + + + + + 64 + 64 + 64 + + + + + + + + + 255 + 255 + 255 + + + + + + + 64 + 64 + 64 + + + + + + + + + 64 + 64 + 64 + + + + + + + 64 + 64 + 64 + + + + + + + + Form + + + + 6 + + + 6 + + + 6 + + + 6 + + + 4 + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 0 + 0 + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + 147 + 147 + 147 + + + + + + + + true + + + background-color: rgb(147, 147, 147); + + + + + + + + diff --git a/src/form/pqFontPropertyWidget.ui b/src/form/pqFontPropertyWidget.ui new file mode 100644 index 0000000..f914f9f --- /dev/null +++ b/src/form/pqFontPropertyWidget.ui @@ -0,0 +1,206 @@ + + + FontPropertyWidget + + + + 0 + 0 + 500 + 136 + + + + + 500 + 0 + + + + Text Property + + + + + + + 1 + 0 + + + + Specify the path to a TTF file here. + + + + + + + 2 + + + + + Apply + + + + :/pqWidgets/Icon/pq/pqApply.png:/pqWidgets/Icon/pq/pqApply.png + + + true + + + + + + + Resets any changed properties to their values from the last time 'Apply' was clicked. + + + Reset + + + + :/pqWidgets/Icon/pq/pqCancel.png:/pqWidgets/Icon/pq/pqCancel.png + + + + + + + Delete + + + + :/pqWidgets/Icon/pq/pqDelete.png:/pqWidgets/Icon/pq/pqDelete.png + + + + + + + + + + + + Select font + + + QComboBox::AdjustToContents + + + 2 + + + + + + + Set font size + + + 1 + + + + + + + true + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + + + + + + + Set font opacity + + + 1.000000000000000 + + + 0.100000000000000 + + + + + + + Bold + + + ... + + + + :/pqWidgets/Icon/pq/pqBold24.png:/pqWidgets/Icon/pq/pqBold24.png + + + true + + + + + + + Italics + + + ... + + + + :/pqWidgets/Icon/pq/pqItalics24.png:/pqWidgets/Icon/pq/pqItalics24.png + + + true + + + + + + + Shadow + + + ... + + + + :/pqWidgets/Icon/pq/pqShadow24.png:/pqWidgets/Icon/pq/pqShadow24.png + + + true + + + + + + + + + + + + + diff --git a/src/form/pqVCRToolbar.ui b/src/form/pqVCRToolbar.ui new file mode 100644 index 0000000..7b41945 --- /dev/null +++ b/src/form/pqVCRToolbar.ui @@ -0,0 +1,101 @@ + + + VCRToolbar + + + + 0 + 0 + 250 + 40 + + + + + 250 + 40 + + + + VCR Controls + + + Qt::Horizontal + + + true + + + + false + + + + :/pqWidgets/Icon/pq/pqVcrPlay.png:/pqWidgets/Icon/pq/pqVcrPlay.png + + + &Play + + + + + false + + + + :/pqWidgets/Icon/pq/pqVcrBack.png:/pqWidgets/Icon/pq/pqVcrBack.png + + + Pre&vious Frame + + + true + + + + + false + + + + :/pqWidgets/Icon/pq/pqVcrFirst.png:/pqWidgets/Icon/pq/pqVcrFirst.png + + + &First Frame + + + + + false + + + + :/pqWidgets/Icon/pq/pqVcrForward.png:/pqWidgets/Icon/pq/pqVcrForward.png + + + &Next Frame + + + + + false + + + + :/pqWidgets/Icon/pq/pqVcrLast.png:/pqWidgets/Icon/pq/pqVcrLast.png + + + &Last Frame + + + + + + + + + + + + + diff --git a/src/include/QDicomViewer.h b/src/include/QDicomViewer.h new file mode 100644 index 0000000..2f4c544 --- /dev/null +++ b/src/include/QDicomViewer.h @@ -0,0 +1,83 @@ +#pragma once +#include +#include "ui_QDicomViewer.h" +#include "importwidget.h" +#include +#include "exportdialog.h" +#include "Customwindow.h" +#include "pqFontPropertyWidget.h" + +class QDicomViewer : public QMainWindow +{ + Q_OBJECT + +public: + + explicit QDicomViewer(QWidget *parent = Q_NULLPTR); + ~QDicomViewer(); + + +public slots: + void Slot_ToolbarVisibilityChanged(bool); + void openDICOMFromPACS(int,std::string); +private: + Ui::QDicomViewerClass *ui; + + void loadStyleSheet(const QString &sheetName); + void Initial(); + void createToolButton(); + + void SetupFileTool(QToolButton* fileBtn); + void SetupImportTool(QToolButton* importBtn); + void SetupExportTool(QToolButton *saveBtn); + + void SetupGridTool(QToolButton *gridBtn); + void SetupSyncTool(QToolButton *syncBtn); + void SetupAnnoTool(QToolButton *annoBtn); + + void SetupSliceTool(QToolButton* sliceBtn); + void SetupAdjustTool(QToolButton *winlevelBtn); + void SetupPanTool(QToolButton* panBtn); + void SetupZoomTool(QToolButton* zoomBtn); + void SetupMeasureTool(QToolButton *measureBtn); + + void SetupFlipTool(QToolButton *flipBtn); + void SetupFusionTool(QToolButton* fusionBtn); + void SetupCineTool(QToolButton* cineBtn); + void SetupEmptyTool(QToolButton* emptyBtn); + + + void SetupFullScreenTool(QToolButton *btnfullscreen); + void SetupMaximizeTool(QToolButton *btnmaximize); + void SetupMinimizeTool(QToolButton *btnminimize); + void SetupCloseTool(QToolButton *btnclose); + + + void displayThumbnailBar(bool value); + void drawDICOM(const std::string &dicomName, SeriesOpenMode openMode); + void setConnections(); + void executeActiveMeasure(ViewContainerWidget *Container, AnnotationActorType annType); + void createVCRToolbar(DicomImageView *v); + + QIcon icon_manual; + QIcon icon_auto; + QIcon icon_dis; + QAction *m_sync_item_action[SYNC_ITEM_NUM]; + QAction* m_sync_state_action; + QAction* m_measure_hidden_action; + QAction* m_patient_hidden_action; + QAction* m_preview_display_action; + + int act_num_of_close; + int act_num_of_maximize; + int act_num_of_fullscreen; + int act_num_of_minimize; + + ExportDialog *exportDialog = nullptr; + ImportWidget *m_import =nullptr; + QSettings m_qs; + Customwindow *m_customwin =nullptr; + + AnnotationActorType m_cur_measure = AnnotationActorType::RulerAnn; + +}; diff --git a/src/include/base/DicomLoader.h b/src/include/base/DicomLoader.h new file mode 100644 index 0000000..81cea6b --- /dev/null +++ b/src/include/base/DicomLoader.h @@ -0,0 +1,86 @@ +#pragma once +#include "global/include_all.h" +#include "global/include_vitk.h" + +using namespace std; +typedef std::map> OrienMapType; +class OrienHelper +{ +public: + static std::string StringFilter(char* str); + static void init(); + static std::vector* getOrienStrList(const std::string &index); + static OrienMapType orien_map; +}; + +class SeriesInstance; + + + + + +class DicomLoader { +public: + + static DicomLoader *GetInstance(); + + + static void itkReaderProCallbackFunction_FILE(itk::ProcessObject* obj, const itk::ProgressEvent&, void* data); + static void itkReaderProCallbackFunction_DIR(itk::ProcessObject* obj, const itk::ProgressEvent&, void* data); + static void itkReaderEndCallbackFunction(itk::ProcessObject* obj, const itk::ProgressEvent&, void* data); + + + DicomTagInfo_t* createDicomTagsInfo(); + void ItkPreReadSeries(const std::string &dicomName, SeriesOpenMode openMode); + UniqueIDInfo_t* createUniqueID(const std::string &dicomName, SeriesOpenMode openMode); + bool IsDuplicate(UniqueIDInfo_t* unique); + SeriesInstance* createSeries(UniqueIDInfo_t* uniqueID, DicomTagInfo_t* tag_info, + vtkGenericOpenGLRenderWindow* gl_rewin, bool copy = false); + AddDicomType getAddDicomType()const + { + return m_addType; + } + const PatientsMapType &getPatientsList() + { + return m_patients; + } + + + //if not duplicate + void InitFromRead(SeriesInstance* instance);// , DicomTagInfo_t* tag_info); + void InitFromCopy(SeriesInstance* instance); + + + bool deleteSeriesInstance(SeriesInstance* old); + SeriesInstance* addSeriesInstance(SeriesInstance* instance, bool copy =false); + + SeriesInfo_t* getSerieInfo(const UniqueIDInfo &uniqueID); + SeriesInstance* getFirstInstance(const UniqueIDInfo &uniqueID); + InstancesVecType* getInstancesVec(const UniqueIDInfo &uniqueID); + //for export use + void getOpenedInstancesVec(InstancesVecType& retVec); + + //void deleteSeriesInstance(SeriesInstance* instance,bool from_map = true); + void deleteInstanceFromMap(UniqueIDInfo uniqueID); + void setDirObservers(void* client); + void setFileObservers(void* client); + +private: + explicit DicomLoader(); + ~DicomLoader(); + static DicomLoader *instance; + //void copyDicomTagsInfo(SeriesInstance* origin, SeriesInstance* copy); + + + //once + ConnectorType::Pointer m_itkConnector; + SeriesReaderType::Pointer m_itkSeriesReader; + ImageIOType::Pointer m_gdcmIO; + InputNamesGeneratorType::Pointer m_inputNames; + + + AddDicomType m_addType; + PatientsMapType m_patients; + +}; + diff --git a/src/include/base/dicomviewerbase.h b/src/include/base/dicomviewerbase.h new file mode 100644 index 0000000..379d109 --- /dev/null +++ b/src/include/base/dicomviewerbase.h @@ -0,0 +1,14 @@ +#ifndef _DICOM_VIEWER_BASE_H_ +#define _DICOM_VIEWER_BASE_H_ + +#define SINGLETON_DEFINE(ClassName) \ +static ClassName& GetInstance() \ +{ \ + static ClassName type_instance; \ + return type_instance; \ +} \ + \ +ClassName(const ClassName&) = delete; \ +ClassName& operator=(const ClassName&) = delete \ + +#endif diff --git a/src/include/base/dicomviewerhelper.h b/src/include/base/dicomviewerhelper.h new file mode 100644 index 0000000..7fd5e63 --- /dev/null +++ b/src/include/base/dicomviewerhelper.h @@ -0,0 +1,59 @@ +锘#ifndef _DICOM_VIEWER_HELPER_H_ +#define _DICOM_VIEWER_HELPER_H_ + +#include "dicomviewerbase.h" +#include +#include +#include +#include +#include + + +struct host { + QString name; + QString ae; + QString ip; + QString port; +}; + + +class DicomViewerProductCfg +{ +public: + SINGLETON_DEFINE(DicomViewerProductCfg); + + QString ourTitle(); + bool setOurTitle(QString ae); + QString ourPort(); + bool setOurPort(QString port); + bool pacsInfo(QList& info); + bool setPacsInfo(QList& info); + + +private: + DicomViewerProductCfg(); + ~DicomViewerProductCfg(); + bool loadcfg(); + bool savecfg(); +private: + bool m_bLoaded; + QJsonDocument m_JsonDocument; + QJsonObject m_JsonRootObject; +}; + + +class DicomViewerHelper +{ +public: + static QString applicationPath(); + static QString ourTitle(); + static bool setOurTitle(QString ae); + static QString ourPort(); + static bool setOurPort(QString port); + static bool pacsInfo(QList& info); + static bool setPacsInfo(QList& info); + +}; + + +#endif diff --git a/src/include/base/dicomviewertype.h b/src/include/base/dicomviewertype.h new file mode 100644 index 0000000..2220112 --- /dev/null +++ b/src/include/base/dicomviewertype.h @@ -0,0 +1,30 @@ +#ifndef _DICOMVIEWER_TYPE_H_ +#define _DICOMVIEWER_TYPE_H_ + +#include + +struct PACSStudyInfo { + QString studyDate; + QString patientName; + QString patientID; + QString accessionNumber; + QString patientBirthDate; + QString patientSex; + QString patientAge; + QString studyInstanceUID; + QString studyID; + QString requestingPhysician; +}; + +struct PACSSeriesInfo { + QString patientName; + QString studyInstanceUID; + QString studyID; + QString seriesUID; + QString modality; + QString seriesNumber; +}; + + +#endif // !_DICOMVIERER_TYPE_H_ + diff --git a/src/include/base/infinitiViewer.h b/src/include/base/infinitiViewer.h new file mode 100644 index 0000000..65cddc9 --- /dev/null +++ b/src/include/base/infinitiViewer.h @@ -0,0 +1,389 @@ +#ifndef infinitiViewer_h +#define infinitiViewer_h + +#include "vtkInteractionImageModule.h" // For export macro +#include "vtkObject.h" +#include "vector" +#include "QList" +#include "DraggableActor.h" + +//for convert vtkEvent to Qt signal +#include "vtkSignalRaiser.h" +#include "RulerLegendActor.h" +#include "QGlobals.h" + +class vtkAlgorithm; +class vtkAlgorithmOutput; +class vtkImageSlice; +class vtkLookupTable; +class vtkScalarsToColors; +class vtkImageSliceMapper; +class vtkImageData; +class vtkInformation; +class ActorDraggableInteractorStyle; +class vtkRenderWindow; +class vtkRenderer; +class vtkRenderWindowInteractor; + +class MeasureStore; + +#define IN_TEST_MODE + + +#ifdef IN_TEST_MODE +class vtkScalarBarActor; +#endif + +class vtkCornerAnnotation; +class Measure; +class infinitiViewer : public vtkObject +{ +public: + static infinitiViewer* New(); + vtkTypeMacro(infinitiViewer, vtkObject); + void PrintSelf(ostream& os, vtkIndent indent) override; + + + enum infinitiViewerEvents + { + SlicedEvent = vtkCommand::UserEvent + 500, + }; + + //vtkGetMacro(m_cornerAnnotation, vtkCornerAnnotation); + vtkCornerAnnotation* GetvtkCornerAnnotation() + { + return cornerAnnotation; + } + void updateCornerInfo(int index); + void updateCornerInfoAll(); + void initCornerInfo(DicomTagInfo_t *pSeriesTags); + void updateOrienInfo(TransFormType type); + void setUpImageViewer(); + + //For Export + void initTopLeftCornerInfo(const std::string &lbl_ser_num, const std::string &SeriesNumber); + /** + * Get the name of rendering window. + */ + virtual const char* GetWindowName(); + + /** + * Render the resulting image. + */ + virtual void Render(void); + + //@{ + /** + * Set/Get the input image to the viewer. + */ + virtual void SetInputData(vtkImageData* in); + virtual vtkImageData* GetInput(); + virtual void SetInputConnection(vtkAlgorithmOutput* input); + + //@} + + /** + * Set/get the slice orientation + */ + + enum + { + SLICE_ORIENTATION_YZ = 0, + SLICE_ORIENTATION_XZ = 1, + SLICE_ORIENTATION_XY = 2 + }; + + vtkGetMacro(SliceOrientation, int); + virtual void SetSliceOrientation(int orientation); + + virtual void SetSliceOrientationToXY() + { + this->SetSliceOrientation(infinitiViewer::SLICE_ORIENTATION_XY); + } + + virtual void SetSliceOrientationToYZ() + { + this->SetSliceOrientation(infinitiViewer::SLICE_ORIENTATION_YZ); + } + + virtual void SetSliceOrientationToXZ() + { + this->SetSliceOrientation(infinitiViewer::SLICE_ORIENTATION_XZ); + } + + //@{ + /** + * Set/Get the current slice to display (depending on the orientation + * this can be in X, Y or Z). + */ + virtual int GetSlice(); + virtual void SetSlice(int s); + //@} + + //@{ + /** + * Return the minimum and maximum slice values (depending on the orientation + * this can be in X, Y or Z). + */ + virtual int GetSliceMin(); + virtual int GetSliceMax(); + virtual void GetSliceRange(int range[2]) { this->GetSliceRange(range[0], range[1]); } + virtual void GetSliceRange(int& min, int& max); + virtual int* GetSliceRange(); + //@} + + //@{ + /** + * Set window and level for mapping pixels to colors. + */ + virtual double GetColorWindow(); + virtual double GetColorLevel(); + virtual void SetColorWindow(double s); + virtual void SetColorLevel(double s); + void SetNegativeMode(bool negative); + void SetLookupTable(vtkLookupTable *lut); + //@} + + //@{ + /** + * These are here when using a Tk window. + */ + virtual void SetDisplayId(void* a); + virtual void SetWindowId(void* a); + virtual void SetParentId(void* a); + //@} + + void SetZoomScale(double scale); + void SetPanOffset(double* point); + //@{ + /** + * Get the position (x and y) of the rendering window in + * screen coordinates (in pixels). + */ + virtual int* GetPosition() VTK_SIZEHINT(2); + + /** + * Set the position (x and y) of the rendering window in + * screen coordinates (in pixels). This resizes the operating + * system's view/window and redraws it. + */ + virtual void SetPosition(int x, int y); + virtual void SetPosition(int a[2]) { this->SetPosition(a[0], a[1]); } + //@} + + //@{ + /** + * Get the size (width and height) of the rendering window in + * screen coordinates (in pixels). + */ + virtual int* GetSize() VTK_SIZEHINT(2); + + /** + * Set the size (width and height) of the rendering window in + * screen coordinates (in pixels). This resizes the operating + * system's view/window and redraws it. + * + * If the size has changed, this method will fire + * vtkCommand::WindowResizeEvent. + */ + virtual void SetSize(int width, int height); + virtual void SetSize(int a[2]) { this->SetSize(a[0], a[1]); } + //@} + + //@{ + /** + * Get the internal render window, renderer, image actor, and + * image map instances. + */ + vtkGetObjectMacro(RenderWindow, vtkRenderWindow); + vtkGetObjectMacro(Renderer, vtkRenderer); + vtkGetObjectMacro(ImageActor, vtkImageSlice); + vtkGetObjectMacro(ImageMapper, vtkImageSliceMapper); + vtkGetObjectMacro(InteractorStyle, ActorDraggableInteractorStyle); + //@} + + //@{ + /** + * Set your own renderwindow and renderer + */ + virtual void SetRenderWindow(vtkRenderWindow* arg); + virtual void SetRenderer(vtkRenderer* arg); + //@} + + /** + * Attach an interactor for the internal render window. + */ + virtual void SetupInteractor(vtkRenderWindowInteractor*); + + //@{ + /** + * Create a window in memory instead of on the screen. This may not + * be supported for every type of window and on some windows you may + * need to invoke this prior to the first render. + */ + virtual void SetOffScreenRendering(vtkTypeBool); + virtual vtkTypeBool GetOffScreenRendering(); + vtkBooleanMacro(OffScreenRendering, vtkTypeBool); + //@} + + + vtkGetMacro(Fusion, vtkTypeBool); + vtkSetMacro(Fusion, vtkTypeBool); + //@{ + /** + * Fusion On and Off,if fusion is on ,the renderer will try to show Fusion Data, + * which need to be set with SetFusionInputData function. Without SetFusionInputData, + * there will no Fusion Data show. + */ + vtkBooleanMacro(Fusion, vtkTypeBool); + //@} + + //@{ + /** + * Fusion Opacity, default is 0.5 + */ + vtkGetMacro(FusionOpacity, double); + //@} + void SetFusionOpacity(double); + void IncreFusionOpacity(double); + void SetScalarBarTitle(double); + //@{ + /** + * Set a imageData to fusion with current rendered image. + */ + void SetFusionInputData(vtkImageData*); + //@} + + //@{ + /** + * Set color level for fusion lookupTable, only will take effect after SetFusionInputData. + */ + void SetFusionColorLeveL(double level); + //@} + + //@{ + /** + * Set color window for fusion lookupTable, only will take effect after SetFusionInputData. + */ + void SetFusionColorWindow(double window); + //@} + + //@{ + /** + * Set a ColorTable for fusion, only will take effect after SetFusionInputData. + */ + void SetFusionColorTable(vtkScalarsToColors*); + //@} + //@{ + /** + * Set a Preset ColorTable for fusion, only will take effect after SetFusionInputData. + */ + void SetFusionColorPreset(const char *preset); + //@} + //@} + //@{ + /** + * Switch fusion color-table to next Preset + */ + void SwitchToNextPreset(); + //@} + // + //@{ + /** + * Remove fusion from viewer + */ + void RemoveFusionData(); + //@} + + //@{ + /** + * Remove fusion actor from viewer + */ + void RemoveFusionActor(); + + void DeleteSelectedMeasure(); + void DeleteCurrentSliceMeasure(); + void DeleteCurrentSeriesMeasure(); + + void ActiveMeasure(Measure* m); + void UnActiveMeasure(); + + //for convert vtkEvent to Qt signal + vtkSignalRaiser* GetSignalRaiser(){ + return &raiser; + } + +protected: + infinitiViewer(); + ~infinitiViewer() override; + + virtual void InstallPipeline(); + virtual void UnInstallPipeline(); + virtual void PrepareFusionColorTable(vtkScalarsToColors* table, bool reset = false); + + vtkRenderWindow* RenderWindow; + vtkRenderer* Renderer; + vtkImageSlice* ImageActor; + vtkImageSlice* FusionActor; + vtkImageSliceMapper* ImageMapper; + vtkImageSliceMapper* FusionMapper; + vtkRenderWindowInteractor* Interactor; + ActorDraggableInteractorStyle* InteractorStyle; + vtkTextActor* OpacityActor; + vtkCornerAnnotation* cornerAnnotation; + + #ifdef IN_TEST_MODE + vtkScalarBarActor* bar; + #endif + + int SliceOrientation; + int FirstRender; + int Slice; + int loadedMeasureSlice; + + vtkTypeBool Fusion = false; + vtkTypeBool firstFusion = true; + double FusionOpacity = 0.5; + + + virtual void UpdateOrientation(); + vtkAlgorithm* GetInputAlgorithm(); + vtkInformation* GetInputInformation(); + void LoadMeasures(){ + LoadMeasures(false); + } + void LoadMeasures(bool forceReload); + void AddMeasures(vtkObject*,unsigned long eventid,void* calldata ); + void RemoveMeasures(vtkObject*,unsigned long eventid,void* calldata ); + +private: + + void updateTopLeftCornerInfo(); + + + + infinitiViewer(const infinitiViewer&) = delete; + void operator=(const infinitiViewer&) = delete; + std::vector> fusion_tf_vector; + //for convert vtkEvent to Qt signal + vtkSignalRaiser raiser; + + QList* list = nullptr; + MeasureStore* measureStore; + + void raiseEvent(vtkObject* sender,unsigned long eventId,void* callData = nullptr){ + raiser.raiseEvent(sender,eventId,callData); + } + + void ClearCurrentSliceMeasure() const; + + void ReloadCurrentSliceMeasure(); + vtkNew ruler; + void RenderRuler(); + int currentPresetIndex=1; + + DicomCornerInfo m_cornerInfo; + char SOP_UID[20]; +}; + +#endif diff --git a/src/include/base/seriesinstance.h b/src/include/base/seriesinstance.h new file mode 100644 index 0000000..efbea17 --- /dev/null +++ b/src/include/base/seriesinstance.h @@ -0,0 +1,106 @@ +锘#pragma once +#include "global/include_all.h" +#include "global/include_vitk.h" +#include "global/QGlobals.h" +#include "ActorDraggableInteractorStyle.h" +//#include "QVTKWidget.h" +#include + +class myQVTKOpenGLNativeWidget; +class DicomLoader; + +class SeriesInstance :public QObject +{ + Q_OBJECT + friend class DicomLoader; + +public: + + SeriesInstance::SeriesInstance(UniqueIDInfo_t* uniqueID, DicomTagInfo_t* tag_info, + vtkGenericOpenGLRenderWindow* glrenWin); + ~SeriesInstance(); + + + void setUpSeriesInstance(); + + //UniqueID + UniqueIDInfo_t* getUniqueID()const { + return m_pUniqueID; + }; + SeriesOpenMode getSerirsOpenMode() const + { + return m_pUniqueID->open_mode; + }; + const char* getCurImageName()const; + const char* getCurSeriesName() const; + + + //SeriesTags + DicomTagInfo_t* getDicomTagInfo()const + { + return m_pSeriesTags; + } + int GetSeriesNumber()const + { + return atoi(m_pSeriesTags->m_SeriesNumber.c_str()); + } + + infinitiViewer* getImageViewer2() + { + return m_imageViewer; + } + + //image data + QPixmap GetPixmap(); + vtkImageData* GetData() + { + return m_image; + }; + + + //camera config + double GetExtent() + { + return m_extent; + } + void getCameraCfg(double *vup, double *camPos) + { + for (int i = 0; i < 3; i++) + { + vup[i] = m_vup[i]; + camPos[i] = m_cameraPosition[i]; + } + } + +//protected: +// vtkNew m_imageViewer; +// vtkNew m_image; + + +private: + QSize labelSizeHint() const { + return QSize(image_label_size, image_label_size); + } + QImage vtkImageDataToQImage(vtkImageData* imageData); + + + vtkSmartPointer m_glrenWin; + + + + vtkSmartPointer m_imageViewer; + vtkSmartPointer m_image; + //vtkSmartPointer m_overlap; + + FileNamesContainerType m_fileNames; + + DicomTagInfo_t* m_pSeriesTags; + UniqueIDInfo_t* m_pUniqueID; + + + static qint32 image_label_size; + + double m_extent; + double m_cameraPosition[3]; + double m_vup[3]; +}; diff --git a/src/include/callback/callbackhelper.h b/src/include/callback/callbackhelper.h new file mode 100644 index 0000000..986fdf7 --- /dev/null +++ b/src/include/callback/callbackhelper.h @@ -0,0 +1,30 @@ +#ifndef _CALLBACKHELPER_H_ +#define _CALLBACKHELPER_H_ + +#include + +class DcmDataset; + +// A helper class which connect callback and uppper Qt application through signal/slot + +class CallbackHelper : public QObject +{ + Q_OBJECT + +public: + explicit CallbackHelper(QObject *parent = Q_NULLPTR); + ~CallbackHelper(); + + void foundResult(int index, DcmDataset *response); + void moveProgress(int progress, int total); + void moveStoreProgress(int code, std::string filename); + + +signals: + void sig_foundResult(int index, DcmDataset *response); + void sig_moveProgress(int progress, int total); + void sig_moveStoreProgress(int code, std::string filename); + +}; + +#endif // !_CALLBACKHELPER_H_ diff --git a/src/include/callback/cfindcallback.h b/src/include/callback/cfindcallback.h new file mode 100644 index 0000000..9c2e961 --- /dev/null +++ b/src/include/callback/cfindcallback.h @@ -0,0 +1,21 @@ +#ifndef _CFIND_CALLBACK_H_ +#define _CFIND_CALLBACK_H_ + +#include "dcm_find.h" +#include "callbackhelper.h" + +class CFindCallback : public dcm_cfind_callback +{ + +public: + CFindCallback(); + virtual ~CFindCallback(); + virtual void callback(T_DIMSE_C_FindRQ *request, int &responseCount, T_DIMSE_C_FindRSP *rsp, DcmDataset *responseIndentifiers); + + void setHelper(CallbackHelper *h); + +private: + CallbackHelper *CFindCallbackHelper; +}; + +#endif diff --git a/src/include/callback/cmovecallback.h b/src/include/callback/cmovecallback.h new file mode 100644 index 0000000..6fa4567 --- /dev/null +++ b/src/include/callback/cmovecallback.h @@ -0,0 +1,24 @@ +#ifndef _CMOVE_CALLBACK_H_ +#define _CMOVE_CALLBACK_H_ + +#include "dcm_move.h" +#include "callbackhelper.h" + +class CMoveCallback : public dcm_cmove_callback +{ + +public: + CMoveCallback(); + ~CMoveCallback(); + + virtual void callback(T_DIMSE_C_MoveRQ *request, int responseCount, T_DIMSE_C_MoveRSP *response); + + void setHelper(CallbackHelper *helper); + +private: + CallbackHelper *CMoveCallbackHelper; +}; + + +#endif // !_CMOVE_CALLBACK_H_ + diff --git a/src/include/callback/cmovestorescpcallback.h b/src/include/callback/cmovestorescpcallback.h new file mode 100644 index 0000000..4887d01 --- /dev/null +++ b/src/include/callback/cmovestorescpcallback.h @@ -0,0 +1,27 @@ +#ifndef _CMOVE_STORESCP_CALLBACK_H_ +#define _CMOVE_STORESCP_CALLBACK_H_ + +#include "dcm_move.h" +#include "callbackhelper.h" + +class CMoveStoreSCPCallback : public dcm_cmove_storescp_callback +{ + +public: + CMoveStoreSCPCallback(std::string outDirectory); + ~CMoveStoreSCPCallback(); + + virtual void callback(T_DIMSE_StoreProgress *progress, T_DIMSE_C_StoreRQ *request, char *imageFileName, DcmDataset **imageDataSet, T_DIMSE_C_StoreRSP *response, DcmDataset **statusDetail); + + void setHelper(CallbackHelper *helper); + +private: + CallbackHelper *CMoveStoreScpCallbackHelper; + std::string outDirectory_; + +}; + + + +#endif // !_CMOVE_STORESCP_CALLBACK_H_ + diff --git a/src/include/cine/pqVCRController.h b/src/include/cine/pqVCRController.h new file mode 100644 index 0000000..339071e --- /dev/null +++ b/src/include/cine/pqVCRController.h @@ -0,0 +1,83 @@ +/*========================================================================= + + Program: ParaView + Module: pqVCRController.h + + Copyright (c) 2005-2008 Sandia Corporation, Kitware Inc. + All rights reserved. + + ParaView is a free software; you can redistribute it and/or modify it + under the terms of the ParaView license version 1.2. + + See License_v1.2.txt for the full ParaView license. + A copy of this license can be obtained by contacting + Kitware Inc. + 28 Corporate Drive + Clifton Park, NY 12065 + USA + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=========================================================================*/ + +#ifndef _pqVCRController_h +#define _pqVCRController_h + +//#include "pqComponentsModule.h" +#include +#include +#include +#include "DicomImageView.h" +// pqVCRController is the QObject that encapsulates the +// VCR control functionality. +// It provides a slot to set the scene that this object +// is using for animation. Typically, one would connect this +// slot to a pqAnimationManager like object which keeps track +// of the active animation scene. +class pqVCRController : public QObject +{ + Q_OBJECT +public: + pqVCRController(QObject* parent = nullptr,DicomImageView* v = nullptr); + ~pqVCRController() override; +Q_SIGNALS: + //void playing(bool); + //void timeRanges(double, double); + + void tick(); + void Signal_OnPlaying(bool); + void Signal_FPS(int); + +public Q_SLOTS: + // Connect these signals to appropriate VCR buttons. + void reConnect(); + void onFirstFrame(); + void onPreviousFrame(); + void onNextFrame(); + void onLastFrame(); + void onPlay(int fps); + void onPause(); + +private Q_SLOTS : + void onTick(); + + +private: + Q_DISABLE_COPY(pqVCRController) + QTimer *m_timer; + bool m_cplaying = false; + int m_cfps = 10; + DicomImageView *m_view =nullptr; +}; + +#endif diff --git a/src/include/cine/pqVCRToolbar.h b/src/include/cine/pqVCRToolbar.h new file mode 100644 index 0000000..f8ca990 --- /dev/null +++ b/src/include/cine/pqVCRToolbar.h @@ -0,0 +1,59 @@ + +#ifndef pqVCRToolbar_h +#define pqVCRToolbar_h +#include +#include +#include "pqVCRController.h" +#include "ui_pqVCRToolbar.h" +#include +#include +namespace Ui { + class VCRToolbar; +} + +/** +* pqVCRToolbar is the toolbar with VCR controls. +* Simply instantiate this and put it in your application UI file or +* QMainWindow to use it. +*/ +class DicomImageView; +class pqVCRToolbar : public QToolBar +{ + Q_OBJECT +public: + explicit pqVCRToolbar(QWidget *parent = Q_NULLPTR); + ~pqVCRToolbar(); + void reConnectController(pqVCRController *ctrl); + +public Q_SLOTS: + void setImageView(DicomImageView*); + //virtual void setVisible(bool visible); + +Q_SIGNALS: + void Signal_Onplay(int); + void Signal_OnPause(); + +private Q_SLOTS: + void VCRPlay(); + void onPlaying(bool); + void setEnabled(bool); + void loadFPS(int); + void setFPS(int); + void Display(); + + +private: + + pqVCRController* Controller =nullptr; + Ui::VCRToolbar ui; + QSpinBox *spin_fps; + //QLineEdit *led_display; + + bool m_playing = false; + int m_fps =10; + int slice_num = 0; + + +}; + +#endif diff --git a/src/include/dialog/promptdialog.h b/src/include/dialog/promptdialog.h new file mode 100644 index 0000000..bdac49a --- /dev/null +++ b/src/include/dialog/promptdialog.h @@ -0,0 +1,57 @@ +#ifndef _PROMPT_DIALOG_H_ +#define _PROMPT_DIALOG_H_ + +#include +#include +#include + +class QVBoxLayout; +class QHBoxLayout; +class QLabel; +class QPushButton; +class QSpacerItem; + +class PromptDialog : public QDialog +{ + Q_OBJECT + +public: + explicit PromptDialog(QWidget *parent = Q_NULLPTR); + PromptDialog(const QString& msg, QWidget *parent = Q_NULLPTR); + ~PromptDialog(); + + void setMsg(const QString& msg); + +public slots: + void close(); + + void onTitleBarDestroyed(); + +protected: + virtual bool eventFilter(QObject *obj, QEvent *event); + +private: + void initUi(); + void initSys(); + +private: + QVBoxLayout *m_pMainLayout; + QWidget *m_pTitleBar; + QWidget *m_pMsgWidget; + QHBoxLayout *m_pMsgLayout; + QLabel *m_pMsgLabel; + QWidget *m_pActionWidget; + QHBoxLayout *m_pActionLayout; + QSpacerItem *m_pActionSpacerItem; + QPushButton *m_pConfirmButton; + +private: + struct + { + bool dragging; + QPoint dragStartPosition; + } m_dragState; +}; + +#endif // !_PROMPT_DIALOG_H_ + diff --git a/src/include/dialog/promptdialogtitlebar.h b/src/include/dialog/promptdialogtitlebar.h new file mode 100644 index 0000000..1cebbf1 --- /dev/null +++ b/src/include/dialog/promptdialogtitlebar.h @@ -0,0 +1,38 @@ +#ifndef _PROMPT_DIALOG_TITLEBAR_H_ +#define _PROMPT_DIALOG_TITLEBAR_H_ + +#include + +class QHBoxLayout; +class QLabel; +class QPushButton; +class QSpacerItem; + +class PromptTitleBar : public QWidget +{ + Q_OBJECT + +public: + explicit PromptTitleBar(QWidget *parent = Q_NULLPTR); + ~PromptTitleBar(); + + void setTitleText(const QString& title); + + +signals: + void sigClose(); + +private: + void initUi(); + void initSys(); + +private: + QHBoxLayout *m_pMainLayout; + QLabel *m_pLogoLabel; + QLabel *m_pTitleLabel; + QSpacerItem *m_pSpacerItem; + QPushButton *m_pCloseButton; +}; + +#endif // !_PROMPT_DIALOG_TITLEBAR_H_ + diff --git a/src/include/dialog/qxtspanslider.h b/src/include/dialog/qxtspanslider.h new file mode 100644 index 0000000..eaee4d6 --- /dev/null +++ b/src/include/dialog/qxtspanslider.h @@ -0,0 +1,112 @@ +#ifndef QXTSPANSLIDER_H +/**************************************************************************** +** Copyright (c) 2006 - 2011, the LibQxt project. +** See the Qxt AUTHORS file for a list of authors and copyright holders. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the LibQxt project nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +** +*****************************************************************************/ + +#define QXTSPANSLIDER_H + +#include +#include + +//#include "qxtnamespace.h" +//#include "qxtglobal.h" + +class QxtSpanSliderPrivate; + +class QxtSpanSlider : public QSlider { + Q_OBJECT + + //QXT_DECLARE_PRIVATE(QxtSpanSlider) + Q_PROPERTY(int lowerValue READ lowerValue WRITE setLowerValue) + Q_PROPERTY(int upperValue READ upperValue WRITE setUpperValue) + Q_PROPERTY(int lowerPosition READ lowerPosition WRITE setLowerPosition) + Q_PROPERTY(int upperPosition READ upperPosition WRITE setUpperPosition) + Q_PROPERTY(HandleMovementMode handleMovementMode READ handleMovementMode WRITE setHandleMovementMode) + Q_ENUMS(HandleMovementMode) + +public: + explicit QxtSpanSlider(QWidget* parent = 0); + explicit QxtSpanSlider(Qt::Orientation orientation, QWidget* parent = 0); + virtual ~QxtSpanSlider(); + + enum HandleMovementMode + { + FreeMovement, + NoCrossing, + NoOverlapping + }; + + enum SpanHandle + { + NoHandle, + LowerHandle, + UpperHandle + }; + + HandleMovementMode handleMovementMode() const; + void setHandleMovementMode(HandleMovementMode mode); + + int lowerValue() const; + int upperValue() const; + + int lowerPosition() const; + int upperPosition() const; + +public Q_SLOTS: + void setLowerValue(int lower); + void setUpperValue(int upper); + void setSpan(int lower, int upper); + + void setLowerPosition(int lower); + void setUpperPosition(int upper); + +Q_SIGNALS: + void spanChanged(int lower, int upper); + void lowerValueChanged(int lower); + void upperValueChanged(int upper); + + void lowerPositionChanged(int lower); + void upperPositionChanged(int upper); + + void sliderPressed(SpanHandle handle); + +protected: + virtual void keyPressEvent(QKeyEvent* event); + virtual void mousePressEvent(QMouseEvent* event); + virtual void mouseMoveEvent(QMouseEvent* event); + virtual void mouseReleaseEvent(QMouseEvent* event); + virtual void paintEvent(QPaintEvent* event); + +private: + QxtSpanSliderPrivate* d_ptr; + friend class QxtSpanSliderPrivate; + +}; + +#endif // QXTSPANSLIDER_H diff --git a/src/include/dialog/qxtspanslider_p.h b/src/include/dialog/qxtspanslider_p.h new file mode 100644 index 0000000..f960efa --- /dev/null +++ b/src/include/dialog/qxtspanslider_p.h @@ -0,0 +1,84 @@ +#ifndef QXTSPANSLIDER_P_H +/**************************************************************************** +** Copyright (c) 2006 - 2011, the LibQxt project. +** See the Qxt AUTHORS file for a list of authors and copyright holders. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the LibQxt project nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +** +*****************************************************************************/ + +#define QXTSPANSLIDER_P_H + +#include +#include +#include "qxtspanslider.h" + +QT_FORWARD_DECLARE_CLASS(QStylePainter) +QT_FORWARD_DECLARE_CLASS(QStyleOptionSlider) + +class QxtSpanSliderPrivate : public QObject { + Q_OBJECT +public: + QxtSpanSliderPrivate(); + void initStyleOption(QStyleOptionSlider* option, QxtSpanSlider::SpanHandle handle = QxtSpanSlider::UpperHandle) const; + int pick(const QPoint& pt) const + { + return q_ptr->orientation() == Qt::Horizontal ? pt.x() : pt.y(); + } + int pixelPosToRangeValue(int pos) const; + void handleMousePress(const QPoint& pos, QStyle::SubControl& control, int value, QxtSpanSlider::SpanHandle handle); + void drawHandle(QStylePainter* painter, QxtSpanSlider::SpanHandle handle) const; + void setupPainter(QPainter* painter, Qt::Orientation orientation, qreal x1, qreal y1, qreal x2, qreal y2) const; + void drawSpan(QStylePainter* painter, const QRect& rect) const; + void triggerAction(QAbstractSlider::SliderAction action, bool main); + void swapControls(); + + int lower; + int upper; + int lowerPos; + int upperPos; + int offset; + int position; + QxtSpanSlider::SpanHandle lastPressed; + QxtSpanSlider::SpanHandle mainControl; + QStyle::SubControl lowerPressed; + QStyle::SubControl upperPressed; + QxtSpanSlider::HandleMovementMode movement; + bool firstMovement; + bool blockTracking; + + +public Q_SLOTS: + void updateRange(int min, int max); + void movePressedHandle(); + +private: + QxtSpanSlider* q_ptr; + friend class QxtSpanSlider; + + +}; + +#endif // QXTSPANSLIDER_P_H diff --git a/src/include/export/dicomexporter.h b/src/include/export/dicomexporter.h new file mode 100644 index 0000000..b59fad5 --- /dev/null +++ b/src/include/export/dicomexporter.h @@ -0,0 +1,122 @@ +#ifndef DICOMEXPORTER_H +#define DICOMEXPORTER_H + +#include +#include "exportoptions.h" +#include +#include +#include +#include +#include +#include +#include "base/infinitiViewer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "itkMetaDataObject.h" +#include "itkMetaDataDictionary.h" + + +class DicomExporter : public QObject +{ + Q_OBJECT + enum FileType + { + SingleFile, + DirType, + NonType, + + }; +public: + explicit DicomExporter(QObject *parent = nullptr); + + void execute(ExportOptions options); + +signals: + void exportFinished(); + void exportProgress(int total, int progress); + +public slots: + + +private: + //dicom tags + const std::string TAG_WindowLevel = "0028|1050"; + const std::string TAG_WindowWidth = "0028|1051"; + + //anonymization + const std::string TAG_InstitutionName = "0008|0080"; + const std::string TAG_InstitutionAddress = "0008|0081"; + const std::string TAG_ReferringPhysicianName = "0008|0090"; + const std::string TAG_OperatorsName = "0008|1070"; + const std::string TAG_PatientAddress = "0010|1040"; + const std::string TAG_PatientTelephoneNumbers = "0010|2154"; + const std::string TAG_OtherPatientNames = "0010|1001"; + const std::string TAG_OtherPatientIDs = "0010|1000"; + const std::string TAG_OtherPatientIDsSequence = "0010|1002"; + const std::string TAG_PatientName = "0010|0010"; + const std::string TAG_PatientBirthDate = "0010|0030"; + const std::string TAG_PatientSex = "0010|0040"; + const std::string TAG_PatientAge = "0010|1010"; + const std::string TAG_PatientID = "0010|0020"; + const std::string TAG_AccessionNumber = "0008|0050"; + + //bmp jpg png tiff exprt releated objects + static const unsigned int InputDimensionExport = 3; + typedef signed short PixelTypeExport; + typedef itk::Image InputImageTypeExport; + typedef itk::GDCMImageIO ImageIOTypeExport; + typedef itk::ImageSeriesReader SeriesReaderTypeExport; + typedef itk::ImageToVTKImageFilter ConnectorTypeExport; + typedef itk::GDCMSeriesFileNames InputNamesGeneratorTypeExport; + + //dicom export releated objects + typedef signed short OutputPixelType; + static const unsigned int OutputDimension = 2; + typedef itk::Image Image2DType; + typedef itk::ImageSeriesWriter SeriesWriterType; + +private: + void initVTK(); + void initDataReader(); + void loadDicomFileAndRender(int file_type); + //void updateCornerInfo(int slice, int maxslice); + void doExport(int file_type); + QString getFileExtention(); + void writeToFile(const QString& fileName); + void writeToPngFile(vtkSmartPointer windowToImageFilter, const QString& fileName); + void writeToBmpFile(vtkSmartPointer windowToImageFilter, const QString& fileName); + void writeToJpgFile(vtkSmartPointer windowToImageFilter, const QString& fileName); + void writeToTiffFile(vtkSmartPointer windowToImageFilter, const QString& fileName); + void exportPicture(const QString& fileName); + void exportDicom(const QString& fileName); + void exportSingleDicomFile(const QString& fileName); + void exportDicomDirectory(const QString& fileName); + void modifyDicomTags(SeriesReaderTypeExport::DictionaryArrayRawPointer dicArray); + void caculateExportTotalCount(); + +private: + ExportOptions exportOptions; + int totalCount;//the total count of image to be exported + int exportedNumber;//the exported image index + + //itk vtk releated objects + ImageIOTypeExport::Pointer m_gdcmIOExport; + SeriesReaderTypeExport::Pointer m_itkSeriesReaderExport; + ConnectorTypeExport::Pointer m_itkConnectorExport; + InputNamesGeneratorTypeExport::Pointer m_inputNamesExport; + vtkSmartPointer m_glrenWinExport; + vtkSmartPointer m_imageViewerExport; +}; + +#endif // DICOMEXPORTER_H \ No newline at end of file diff --git a/src/include/export/dicomexporterthread.h b/src/include/export/dicomexporterthread.h new file mode 100644 index 0000000..f189fa2 --- /dev/null +++ b/src/include/export/dicomexporterthread.h @@ -0,0 +1,33 @@ +#ifndef DICOMEXPORTERTHREAD_H +#define DICOMEXPORTERTHREAD_H + +#include +#include +#include "exportoptions.h" +#include "dicomexporter.h" + +class DicomExporterThread : public QThread +{ + Q_OBJECT +public: + explicit DicomExporterThread(ExportOptions options, QObject *parent = nullptr); + +signals: + void exportFinished(); + void exportProgress(int total, int progress); + +public slots: + +protected: + virtual void run() override; + +//private: +// void init(); + +private: + ExportOptions exportOptions; + DicomExporter *exporter; + +}; + +#endif // DICOMEXPORTERTHREAD_H \ No newline at end of file diff --git a/src/include/export/exportdialog.h b/src/include/export/exportdialog.h new file mode 100644 index 0000000..bcfc4a6 --- /dev/null +++ b/src/include/export/exportdialog.h @@ -0,0 +1,47 @@ +#ifndef EXPORTDIALOG_H +#define EXPORTDIALOG_H + +#include +#include "exportoptions.h" +#include "qdir.h" +#include "dicomexporterthread.h" +#include "QFileDialog" +#include "DicomLoader.h" +#include "dicomimageview.h" + + + + +namespace Ui { +class ExportDialog; +} + +class ExportDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ExportDialog(QWidget *parent = nullptr); + ~ExportDialog(); + //void setCurSeries(SeriesInstance *serie); + //void setViewContainer(ViewContainerWidget *widget) + void setCurView(DicomImageView *view); + +public slots: + void onBtnExportClicked(); + void onBtnCancelClicked(); + void onBtnSelectFolderClicked(); + void onExportProgress(int totalCount, int progress); + void onExportFinished(); + +private: + void init(); + void initUI(); + +private: + Ui::ExportDialog *ui; + //ViewContainerWidget *viewContainerWidget; + DicomImageView *cur_view; +}; + +#endif // EXPORTDIALOG_H diff --git a/src/include/export/exportoptions.h b/src/include/export/exportoptions.h new file mode 100644 index 0000000..cbf1716 --- /dev/null +++ b/src/include/export/exportoptions.h @@ -0,0 +1,47 @@ +#ifndef EXPORTOPTIONS_H +#define EXPORTOPTIONS_H + +#include +#include +#include + +class SeriesInstance; + +class ExportOptions +{ + +public: + explicit ExportOptions(); + + QList inputData;//the fileName or directory list to export + + int windowWidth;//exported window width + int windowLevel;//exported window level + + //export file format + enum FileFormat{Bmp, Jpeg, Png, Tiff, Dicom}; + //corner annotation option + enum CornerAnnotation {Full, Basic, Disabled}; + //Picture Size + enum PictureSize {Default, CurrentSize, CustomSize}; + + FileFormat exportFileFormat; + + bool isAnonymization;//apply to export dicom file + + CornerAnnotation cornerAnnotation;// apply to export picture file + + QString exportDirectory;//the directory where file to export to + + QString fileNamePrefix;//the prefix of each exported file names + + bool isViewFilesWhenFinished;// whether to open directory when export finished + + PictureSize pictureSizePolicy;//the size of exprted picture + + QSize customPictureSize;//apply when user choose customsize + + SeriesInstance *serie; +}; + +#endif // EXPORTOPTIONS_H \ No newline at end of file diff --git a/src/include/global/QGlobals.h b/src/include/global/QGlobals.h new file mode 100644 index 0000000..d105d81 --- /dev/null +++ b/src/include/global/QGlobals.h @@ -0,0 +1,421 @@ +#pragma once +#include +#include +#include +#include +#include +#include "qevent.h" +#include +#include +#include +#include +#include + +#include +#include + +#include +using namespace std; + +// 软件版本 +#define Project_NAME "EquilibriumNine DicomViewer" +#define Project_VER "1.0.0.0" + +//软件信息 +#define Project_OrganizationName "EquilibriumNine" +#define Project_OrganizationDomain "" + + + +#define SYNC_MANUAL_URL ":/InfiniteViewer/Icon/sync/sync_manual.png" +#define SYNC_AUTO_URL ":/InfiniteViewer/Icon/sync/sync_auto.png" +#define SYNC_DIS_URL ":/InfiniteViewer/Icon/sync/sync_dis.png" + +#define CLOSE_URL ":/InfiniteViewer/Icon/close.png" +#define MAX_URL ":/InfiniteViewer/Icon/max.png" + + +const std::string COLOR_MAP_PATH = "./cfg/ColorMaps.json"; +const double scalarSensitivity = 4.0; + + + +class SeriesInstance; +class thumbnailImage; + +enum DicomModality +{ + ReflMode = 1, + SosMode, + AttMode +}; + +enum CornerPos +{ + BOTTOM_LEFT, + BOTTOM_RIGHT, + TOP_LEFT, + TOP_RIGHT, + BOTTOM_MIDDLE, + RIGHT_MIDDLE, + LEFT_MIDDLE, + TOP_MIDDLE, + TOP_RIGHT_PRIVACY +}; + +enum TransFormType +{ + ROTATE_90_CCW, + ROTATE_90_CW, + ROTATE_180, + H_FLIP, + V_FLIP, + CLEAR +}; + + +enum AnnotationActorType +{ + RulerAnn, + AngleAnn, + ClosedPolygonAnn, + OpenPolygonAnn, + ArrowAnn, + EllipseAnn, + TextAnn, + //HiddenAnn, + DeleteSelectedAnn, + DeleteSliceAnn, + DeleteSeriesAnn +}; + + +namespace USER_CONFIG { + const QSize DEFAULT_TAG_WINDOW_SIZE(800, 600); + const std::string TAG_WINDOW_LEVEL = "0028|1050"; + const std::string TAG_WINDOW_WIDTH = "0028|1051"; + //const std::string TAG_IMAGE_ORIENTATION = "0020|0037"; + + //const std::string TAG_PATIANT_NAME = "0010|0010"; + //const std::string TAG_STUDY_DESCRIPTION = "0008|1030"; + const std::string TAG_SERIES_DESCRIPTION = "0008|103E"; + //const std::string TAG_INSTITUTE_NAME = "0008|0080"; + const std::string TAG_SERIES_NUMBER = "0020|0011"; + + + //unique id + const std::string TAG_PATIENT_NAME = "0010|0010"; + const std::string TAG_STUDY_UID = "0020|000D"; + const std::string TAG_SERIES_UID = "0020|000E"; + + const std::string TAG_STUDY_TIME = "0008|0030"; + const std::string TAG_INSTANCE_NUM = "0020|0013"; + const std::string TAG_PATIANT_BIRTH = "0010|0030"; + const std::string TAG_SLICE_NUM = "0054|0081"; + const std::string TAG_PATIENT_ORIENTATION = "0020|0020"; +} + +#define CORNER_NUM 9 +typedef struct DicomCornerInfo +{ + int win_level; + int win_width; + std::string ConstAnno[CORNER_NUM]; +}DicomCornerInfo_t; + + + + +enum SyncState +{ + AUTO_SYNC, + MANUAL_SYNC, + DIS_SYNC +}; + +enum SyncItem +{ + SLICE_POS, + ZOOM_PAN, + WIDTH_LEVEL +}; + +enum AddDicomType +{ + DUPLICATE_TYPE, + PATINET_LEVEL, + STUDY_LEVEL, + SERIES_LEVEL, + OVERRIDE_LEVEL + //OVERRIDE_SERIES, + //OVERRIDE_IMAGE +}; + + + + + +typedef struct DicomTasInfo +{ + int WL; + int WW; + double spacing[3]; + std::string lbl_ser_num; + + std::string m_dicomName; + std::string m_PatientName; + std::string m_PatientID; + std::string m_PatientBirth; + //std::string m_PatientDOB; + //std::string m_StudyID; + std::string m_StudyDescription; + std::string m_StudyDate; + std::string m_StudyTime; + std::string m_SeriesNumber; + std::string m_SeriesDescription; + std::string m_Modality; + std::string m_SliceNumber; + + //std::string m_BodyPart; + //std::string m_NumberOfSeriesInStudy; + //std::string m_NumberOfStudyRelatedSeries; + //std::string m_PatientSex; + //std::string m_PatientAge; + //std::string m_StudyDate; + //std::string m_Modality; + //std::string m_Manufacturer; + std::string m_Institution; + //std::string m_Model; + //std::string m_ScanOptions; + std::string m_orientation; + //std::string m_StudyInstanceUID; + //std::string m_SeriesInstanceUID; + +}DicomTagInfo_t; + + + +enum SeriesOpenMode { + FILE_OPEN_MODE, + DIR_OPEN_MODE +}; + +typedef struct UniqueIDInfo +{ + std::string patient_name; + std::string study_uid; + std::string series_uid; + SeriesOpenMode open_mode; + std::string instance_num; + std::string dicom_name; + //std::string series_modality; +}UniqueIDInfo_t; + +//typedef std::shared_ptr DicomTagInfo_t*; +//typedef std::shared_ptr UniqueIDInfo_t*; + +namespace DicomUtil +{ + //not include open_mode + static bool EqualsUnique(const UniqueIDInfo &uid1, const UniqueIDInfo & uid2) + { + if (uid1.patient_name == uid2.patient_name && + uid1.study_uid == uid2.study_uid && + uid1.series_uid == uid2.series_uid) { + return true; + } + return false; + } +} + +class FlipExportHelper +{ + +public: + + static bool GetFlip() + { + return flip; + } + static void toggleFlip() + { + flip = !flip; + } + +private: + static bool flip; +}; + +class AnnoHelper +{ + +public: + static bool IsPrivacy() + { + return privacyOn; + } + static void PrivacyOn() + { + privacyOn = true; + } + static void PrivacyOff() + { + privacyOn = false; + } + + static bool IsAnno() + { + return annotOn; + } + static void toggleAnno() + { + annotOn = !annotOn; + } + +private: + static bool privacyOn; + static bool annotOn; +}; + + + +class FontSizeHelper +{ + +public: + static int getSize(QSize size) + { + double ratio = size.width()*1.0 / desktop_width; + int real = font_size - int(25.0*pow(1 - ratio, 2)); + return real; + } + static int desktop_height; + static int desktop_width; + static int font_size; + const int static font_fixed = 25; +}; + +class VCRHelper +{ +public: + static int getVCRXOffset() + { + int x = thumbnailbar_offset * (1 - thumbnailbar_coeff_inv) - vcr_toolbar_offset; + return x; + } + static int getVCRYOffset() + { + int y = toolbar_coeff_inv * toolbar_Height; + return y; + } + static void setThumbnailbar(bool value) + { + if (value == true) + { + thumbnailbar_coeff_inv = 0; + } + else + { + thumbnailbar_coeff_inv = 1; + } + } + + static void setToolbar(bool value) + { + if (value == true) + { + toolbar_coeff_inv = 0; + } + else + { + toolbar_coeff_inv = -1; + } + } + + static const int vcr_toolbar_offset = 115; + static const int toolbar_Height = 50; + static const int toolbtn2_Size = 28; + static const int vcr_toolbar_Width = 220; + +private: + static const int thumbnailbar_offset = 150; + static int toolbar_coeff_inv; + static int thumbnailbar_coeff_inv; +}; + +#define SYNC_ITEM_NUM 3 +#define SYNC_STATE_NUM 3 +class SyncHelper +{ +public: + static bool getSyncItem(SyncItem index) + { + return _syc_item[_syc_state][index]; + } + static void setSyncItem(SyncItem index, bool sync) + { + _syc_item[_syc_state][index] = sync; + } + static void setSyncItems(bool sync) + { + for (int i = 0; i < SYNC_ITEM_NUM; i++) + { + _syc_item[_syc_state][i] = sync; + } + } + static SyncState getSyncState() + { + return _syc_state; + } + static void setSyncState(SyncState curState) + { + _syc_state = curState; + } + static const QString SyncStateName[SYNC_STATE_NUM];// = { "AUTO_SYNC","MANUAL_SYNC","DIS_SYNC" }; + static const QString SyncItemName[SYNC_ITEM_NUM];// = { "SLICE_LOC","ZOOM_PAN","WIDTH_LEVEL" }; +private: + static SyncState _syc_state; + static bool _syc_item[SYNC_STATE_NUM][SYNC_ITEM_NUM]; + static bool _sync_flag; //for just zoom and pan + + +}; + + +typedef std::vector InstancesVecType; + +typedef struct SeriesInfo +{ + SeriesOpenMode open_mode; + std::string instance_num; + bool pixmap_valid; + QPixmap series_pixmap; + UniqueIDInfo_t* unique_info; + DicomTagInfo_t* tag_info; + thumbnailImage* thumb_nail; + InstancesVecType* instances; + SeriesInfo() :pixmap_valid(false), unique_info(nullptr),tag_info(nullptr),thumb_nail(nullptr), instances(nullptr) {} +}SeriesInfo_t; +typedef std::map SeriesMapType; + + +typedef struct StudyInfo +{ + std::string study_date; + std::string study_time; + std::string study_description; + QPushButton *study_label; + SeriesMapType *series; + StudyInfo():study_label(nullptr), series(nullptr) {} +}StudyInfo_t; +typedef std::map StudiesMapType; + +typedef struct PatientInfo +{ + std::string patient_name; + std::string birth_date; + QPushButton *patient_label; + StudiesMapType *studies; + PatientInfo():patient_label(nullptr), studies(nullptr){} +}PatientInfo_t; +typedef std::map PatientsMapType; diff --git a/src/include/global/include_all.h b/src/include/global/include_all.h new file mode 100644 index 0000000..80c914c --- /dev/null +++ b/src/include/global/include_all.h @@ -0,0 +1,9 @@ +#pragma once +#include +#include +#include +#include +#include +#include "QGlobals.h" + + diff --git a/src/include/global/include_dcmtk.h b/src/include/global/include_dcmtk.h new file mode 100644 index 0000000..71adbd1 --- /dev/null +++ b/src/include/global/include_dcmtk.h @@ -0,0 +1,13 @@ +#pragma once + +#include "dcmtk/config/osconfig.h" +#include "dcmtk/dcmdata/dcfilefo.h" +#include "dcmtk/dcmdata/dcdatset.h" +#include "dcmtk/dcmjpeg/djdecode.h" +#include "dcmtk/dcmdata/dcrledrg.h" +#include "dcmtk/dcmdata/dcdeftag.h" +#include "dcmtk/ofstd/ofstdinc.h" +#include "dcmtk/dcmdata/dclist.h" +#include "dcmtk/dcmdata/dcmetinf.h" +#include "dcmtk/dcmdata/dctk.h" + diff --git a/src/include/global/include_vitk.h b/src/include/global/include_vitk.h new file mode 100644 index 0000000..830c908 --- /dev/null +++ b/src/include/global/include_vitk.h @@ -0,0 +1,117 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "infinitiViewer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//screenshot +#include +#include +#include + +//for blending +#include +#include +#include + +#include +//#include +// +//#include "itkCommand.h" +// +//#include +//#include +// +//#include + +//#include "vtkVersion.h" +//#include "vtkImageViewer.h" +//#include "vtkImageMapper3D.h" +//#include "vtkRenderWindowInteractor.h" +//#include "vtkSmartPointer.h" +//#include "vtkImageActor.h" +//#include "vtkInteractorStyleImage.h" +//#include "vtkRenderer.h" +//#include "itkRGBPixel.h" + +//#define NUM_CLASSES 3 +//#define MAX_NUM_ITER 1 + +//#include "itkImageFileReader.h" +//#include "itkImageFileWriter.h" + +//newly added +#include +#include +#include +#include +#include +#include "QVTKInteractorAdapter.h" +//max version:vtk 8.2.0 +//#include + +//min version: vtk 8.2.0 +#include + +//ITK headers +#include +#include +#include +#include +#include +#include +#include +//#include +//#include +//#include + + + + +//newly added0 +#include +#include + +static const unsigned int InputDimension = 3; +static const unsigned int OutputDimension = 2; + +typedef signed short PixelType; +typedef itk::Image InputImageType; +typedef itk::GDCMImageIO ImageIOType; +typedef itk::GDCMSeriesFileNames InputNamesGeneratorType; +typedef itk::ImageToVTKImageFilter ConnectorType; +typedef itk::MetaDataDictionary DictionaryType; +typedef itk::MetaDataObject MetaDataStringType; +typedef itk::ImageSeriesReader SeriesReaderType; + +typedef std::vector< std::string > FilenamesContainer; +typedef FilenamesContainer FileNamesContainerType; + + + +typedef itk::ImageFileWriter ImageWriterType; + +typedef itk::Image< PixelType, OutputDimension > Image2DType; +typedef itk::ImageSeriesWriter SeriesWriterType; + + + diff --git a/src/include/measure/ActorDraggableInteractorStyle.h b/src/include/measure/ActorDraggableInteractorStyle.h new file mode 100644 index 0000000..0105430 --- /dev/null +++ b/src/include/measure/ActorDraggableInteractorStyle.h @@ -0,0 +1,230 @@ +// +// Created by 87714 on 2021/6/19. +// + +#ifndef OMEGAV_ACTORDRAGGABLEINTERACTORSTYLE_H +#define OMEGAV_ACTORDRAGGABLEINTERACTORSTYLE_H + +#include +#include +#include "vtkInteractorStyleImage.h" +#include "vtkNew.h" // For ivars +#include "functional" +#include "vtkCornerAnnotation.h" +#include "vtkCommand.h" + +#define VTKIS_DRAG 33 +#define VTKIS_MEASURE 36 +#define VTKIS_COLORMAP 37 + +#define VTKIS_IMAGE_PAN 5 +#define VTKIS_IMAGE_ZOOM 6 +#define VTKIS_IMAGE_WINDOWLEVEL 7 + + +class vtkProp; +class Measure; +class vtkImageSlice; + +class ActorDraggableInteractorStyle : public vtkInteractorStyleImage { +public: + static ActorDraggableInteractorStyle* New(); + vtkTypeMacro(ActorDraggableInteractorStyle, vtkInteractorStyleImage); + + enum DraggableStyleEvents + { + DragEvent = vtkCommand::UserEvent + 300, + DragEndEvent, + StartMeasureEvent, + EndMeasureEvent, + SlicedEvent, + EndDollyEvent, + PopPropEvent, + DoubleClickEvent, + DeleteMeasureEvent, + RightButtonClickEvent, + ScalarOpacityEvent, + ScalarShiftEvent + }; + + /** + * Called when the user moves the mouse + * Default behavior forwards the event to the observed scene. + */ + void OnMouseMove() override; + + /** + * Called when the user clicks the mouse left button. + * Default behavior forwards the event to the observed scene. + */ + void OnRightButtonDown() override; + void OnLeftButtonDown() override; + + /** + * Called when the user releases the mouse left button. + * Default behavior forwards the event to the observed scene. + */ + void OnLeftButtonUp() override; + + void OnChar() override; + void EndDolly() override; + + + void WindowLevel()override; + + void SetCurrentImageNumber(int i) override; + + vtkSetClampMacro(InteractionMode, int, VTKIS_IMAGE2D, VTKIS_IMAGE_WINDOWLEVEL); + void SetInteractionModeToImage2D() {} + void SetInteractionModeToImage3D() {} + + void SetInteractionModeFromEnum(int InteractionMode) + { + this->UnActiveMeasure(); + this->SetInteractionMode(InteractionMode); + } + + //void SetInteractionModeToImageSlicing() { + // this->UnActiveMeasure(); + // this->SetInteractionMode(VTKIS_IMAGE_SLICING); + //} + //void SetInteractionModeToImagePan(){ + // this->UnActiveMeasure(); + // this->SetInteractionMode(VTKIS_IMAGE_PAN); + //} + //void SetInteractionModeToImageWindowLevel(){ + // this->UnActiveMeasure(); + // this->SetInteractionMode(VTKIS_IMAGE_WINDOWLEVEL); + //} + //void SetInteractionModeToImageZoom(){ + // this->UnActiveMeasure(); + // this->SetInteractionMode(VTKIS_IMAGE_ZOOM); + //} + + + vtkSetObjectMacro(CornerAnnotation, vtkCornerAnnotation); + + vtkProp* GetSelectedProp() + { + return selectedProp; + } + void ClearSelectedProp() { + if (dragProp == selectedProp) dragProp = nullptr; + selectedProp = nullptr; + } + void ActiveMeasure(Measure* m); + void UnActiveMeasure(); + //void SetCornderAnnoEna(bool enable) + //{ + // isCornderAnno = enable; + //} + +protected: + ActorDraggableInteractorStyle(); + ~ActorDraggableInteractorStyle() override; + void StartDrag() { + this->StartState(VTKIS_DRAG); + } + void EndDrag() { + if (this->State != VTKIS_DRAG) + { + return; + } + this->StopState(); + } + void Drag(); + + + void StartColorMapping() + { + OpacityTrigger = false; + ConsumedOpacity = 0; + this->StartState(VTKIS_COLORMAP); + } + + void EndColorMapping(); + void ColorMapping(); + bool OpacityTrigger = false; + + void StartMeasure() { + this->StartState(VTKIS_MEASURE); + } + void EndMeasure() { + if (this->State != VTKIS_MEASURE) + { + return; + } + this->StopState(); + this->InvokeEvent(EndMeasureEvent, this->measure); + } + void StartPan() override; + void EndPan() override; + void EndWindowLevel() override; + void MeasurePlace(); + void NoneStatePick(); + void DispatchEvent(); + + +private: + ActorDraggableInteractorStyle(const ActorDraggableInteractorStyle&) = delete; + void operator=(const ActorDraggableInteractorStyle&) = delete; + vtkNew picker; + + vtkProp* scalarProp = nullptr; + vtkProp* dragProp = nullptr; + vtkProp* selectedProp = nullptr; + int DragStartOrigin[2] = { 0, 0 }; + vtkCornerAnnotation * CornerAnnotation = nullptr; + //bool isCornderAnno = true; + Measure* measure = nullptr; + double PanStartOrigin[3] = { 0.0, 0.0, 0.0 }; + double DollyStartScale = 1.0; + vtkImageSlice * CurrentImageSlice = nullptr; + int lastslice = -1; + + int ScalarStartPosition[2] = { 0, 0 }; + int ScalarCurrentPosition[2] = { 0, 0 }; + double ConsumedOpacity = 0; + + void TestOutPut(vtkObject*, unsigned long eventid, void* calldata) { + switch (eventid) { + case DraggableStyleEvents::SlicedEvent: + { + int* r = (int*)calldata; + + printf("Sliced, current slice number:%d \r\n", r[0]); + + break; + } + case DraggableStyleEvents::EndDollyEvent: { + double *d = (double *)calldata; + + printf("EndDolly, scale param:%f,%f \r\n", d[0], d[1]); + + break; + } + case vtkCommand::EventIds::EndPanEvent: { + double *d = (double *)calldata; + + printf("EndPan, last focalpoint:%f,%f,%f;current focalpoint:%f,%f,%f \r\n", d[0], d[1], d[2], d[3], + d[4], d[5]); + + break; + } + case vtkCommand::EventIds::EndWindowLevelEvent: + { + double *d = (double *)calldata; + + printf("EndWindowLevel, scale param:%f,%f \r\n", d[0], d[1]); + + break; + } + default: + + printf("current event :%s", vtkCommand::GetStringFromEventId(eventid)); + break; + } + } +}; + +#endif //OMEGAV_ACTORDRAGGABLEINTERACTORSTYLE_H diff --git a/src/include/measure/AngleAnnotationActor.h b/src/include/measure/AngleAnnotationActor.h new file mode 100644 index 0000000..16d1d26 --- /dev/null +++ b/src/include/measure/AngleAnnotationActor.h @@ -0,0 +1,83 @@ +// +// Created by on 2021/7/11. +// + +#ifndef OMEGAV_ANGLEANNOTATIONACTOR_H +#define OMEGAV_ANGLEANNOTATIONACTOR_H + + +#include "DraggableActor.h" +#include "Measure.h" + +class ControlPointActor; +class vtkTextProperty; + + +class AngleAnnotationActor : public DraggableActor, public Measure { +public: + //@{ + /** + * Standard methods for instances of this class. + */ + static AngleAnnotationActor *New(); + + vtkTypeMacro(AngleAnnotationActor, DraggableActor + ); + + //@} + void SetRenderer(vtkRenderer *ren); + + void Highlight(int highlightOn) override; + + void BuildShape() override; + + void SetWorldPosition1(double x, double y, double z); + + void SetWorldPosition1(double *pos) { + SetWorldPosition1(pos[0], pos[1], pos[2]); + } + + void SetWorldPosition2(double x, double y, double z); + + void SetWorldPosition2(double *pos) { + SetWorldPosition2(pos[0], pos[1], pos[2]); + } + void SetWorldPosition3(double x, double y, double z); + + void SetWorldPosition3(double *pos) { + SetWorldPosition3(pos[0], pos[1], pos[2]); + } + + bool onMeasureLeftButtonDown(vtkRenderWindowInteractor *) override; + + void onMeasureMouseMove(vtkRenderWindowInteractor *) override; + + bool onMeasureLeftButtonUp(vtkRenderWindowInteractor *) override; + + NextMeasureMacro(AngleAnnotationActor); + + void ForceDelete() override { + this->SetRenderer(nullptr); +#ifdef _DEBUG + printf("LineAnnotationActor delete \r\n"); +#endif // _DEBUG + DraggableActor::Delete(); + } +protected: + AngleAnnotationActor(); + + ~AngleAnnotationActor() override; + +private: + ControlPointActor *controlP1 = nullptr; + ControlPointActor *controlP2 = nullptr; + ControlPointActor *controlP3 = nullptr; + vtkTextProperty* textProperty; + int placedPointCount = 0; + void controlPointCb(vtkObject *, unsigned long event, void *data); + + void selfDragCb(vtkObject *, unsigned long event, void *data); +}; + + +#endif //OMEGAV_ANGLEANNOTATIONACTOR_H \ No newline at end of file diff --git a/src/include/measure/AnnotationActor.h b/src/include/measure/AnnotationActor.h new file mode 100644 index 0000000..42e70fc --- /dev/null +++ b/src/include/measure/AnnotationActor.h @@ -0,0 +1,124 @@ +// +// Created by krad on 2021/6/7. +// + +#ifndef OMEGAV_ANNOTATIONACTOR_H +#define OMEGAV_ANNOTATIONACTOR_H + +#include "vtkProp.h" +#include "vtkPointHandleRepresentation2D.h" +#include "vtkActor2D.h" +#include "vtkAxisActor2D.h" +#include "vtkPolyDataMapper2D.h" +#include "vtkActor2D.h" +#include "vtkCommand.h" + +#include "vtkNew.h" // for ivars +#include "vtkPoints.h" +#include "vtkPointSet.h" +#include + +class vtkPolyData; +class vtkPolyDataMapper2D; +class vtkRenderer; +class AnnotationActor:public vtkProp { +public: + //@{ + /** + * Standard methods for instances of this class. + */ + static AnnotationActor* New(); + vtkTypeMacro(AnnotationActor,vtkProp); + void PrintSelf(ostream& os, vtkIndent indent) override; + //@} + + //@{ + /** + * Handles usually have their coordinates set in display coordinates + * (generally by an associated widget) and internally maintain the position + * in world coordinates. (Using world coordinates insures that handles are + * rendered in the right position when the camera view changes.) These + * methods are often subclassed because special constraint operations can + * be used to control the actual positioning. + */ + virtual void SetDisplayPosition(int index, double pos[3]); + virtual void GetDisplayPosition(int index, double pos[3]); + virtual double* GetDisplayPosition(int index) VTK_SIZEHINT(3); + virtual void SetWorldPosition(int index, double pos[3]); + virtual void GetWorldPosition(int index, double pos[3]); + virtual double* GetWorldPosition(int index) VTK_SIZEHINT(3); + //@} + + virtual void SetRenderer(vtkRenderer *ren); + virtual vtkRenderer* GetRenderer(); + virtual void BuildAnnotation(); + + + void Pick() override; + void MouseEntered(){ + this->InvokeEvent(vtkCommand::EnterEvent,nullptr); + Highlight(1); + }; + void MouseLeave(){ + Highlight(0); + this->InvokeEvent(vtkCommand::LeaveEvent,nullptr); + }; + + virtual void Highlight(int highlightOn); + + //@{ + /** + * Methods to make this class behave as a vtkProp. + */ + double *GetBounds() VTK_SIZEHINT(6) override {return nullptr;} + void GetActors(vtkPropCollection *) override {} + void GetVolumes(vtkPropCollection *) override {} + void ShallowCopy(vtkProp *prop) override {}; + void GetActors2D(vtkPropCollection *) override; + void ReleaseGraphicsResources(vtkWindow *) override; + int RenderOverlay(vtkViewport *viewport) override; + //@} + + int RenderOpaqueGeometry(vtkViewport *vtkNotUsed(viewport)) override {return 0;} + int RenderTranslucentPolygonalGeometry(vtkViewport *vtkNotUsed(viewport)) override {return 0;} + int RenderVolumetricGeometry(vtkViewport *vtkNotUsed(viewport)) override {return 0;} + vtkTypeBool HasTranslucentPolygonalGeometry() override { return 0; } + + + vtkGetObjectMacro(BaseDataPoints,vtkPoints); + vtkSetObjectMacro(BaseDataPoints,vtkPoints); + + void transform(float x,float y); + void ApplyTransform(); + +protected: + AnnotationActor(); + ~AnnotationActor() override; + + vtkPoints* BaseDataPoints; + vtkPoints* renderPoints; + vtkPoints* tempStorePoints = nullptr; + vtkNew renderData; + vtkNew tempData; + //for test + vtkActor2D* actor2D; + vtkActor2D* actor2D2; + vtkPolyDataMapper2D* mapper; + vtkPolyDataMapper2D* mapper2; + vtkRenderer* renderer; + +private: + AnnotationActor(const AnnotationActor&) = delete; + void operator=(const AnnotationActor&) = delete; + void printPick() { + printf("pick!!!\r\n"); + } + void printPick2() { + printf("pick2!!!\r\n"); + } + bool transforming = false; + vtkNew cursor; +}; + + +#endif //OMEGAV_ANNOTATIONACTOR_H diff --git a/src/include/measure/ArrowAnnotationActor.h b/src/include/measure/ArrowAnnotationActor.h new file mode 100644 index 0000000..e0e711d --- /dev/null +++ b/src/include/measure/ArrowAnnotationActor.h @@ -0,0 +1,39 @@ +// +// Created by 87714 on 2021/8/10. +// + +#ifndef OMEGAV_ARROWANNOTATIONACTOR_H +#define OMEGAV_ARROWANNOTATIONACTOR_H + +#include "LineAnnotationActor.h" +class ArrowAnnotationActor: public LineAnnotationActor{ +public: + //@{ + /** + * Standard methods for instances of this class. + */ + static ArrowAnnotationActor* New(); + vtkTypeMacro(ArrowAnnotationActor,LineAnnotationActor); + //@} + + NextMeasureMacro(ArrowAnnotationActor); + void BuildShape() override; + void Highlight(int highlightOn) override; + bool onMeasureLeftButtonDown(vtkRenderWindowInteractor*) override; + +protected: + ArrowAnnotationActor(); + ~ArrowAnnotationActor() override; + void HighlightOn(){ + Highlight(1); + } + void HighlightOff(){ + Highlight(0); + } + +private: + vtkPoints * arrowRenderPoints; +}; + + +#endif //OMEGAV_ARROWANNOTATIONACTOR_H diff --git a/src/include/measure/ControlPointActor.h b/src/include/measure/ControlPointActor.h new file mode 100644 index 0000000..2cc2246 --- /dev/null +++ b/src/include/measure/ControlPointActor.h @@ -0,0 +1,38 @@ +// +// Created by 87714 on 2021/6/22. +// + +#ifndef OMEGAV_CONTROLPOINTACTOR_H +#define OMEGAV_CONTROLPOINTACTOR_H + +#include "DraggableActor.h" + +class ControlPointActor:public DraggableActor { + +public: + //@{ + /** + * Standard methods for instances of this class. + */ + static ControlPointActor* New(); + vtkTypeMacro(ControlPointActor,DraggableActor); + //@} + + void BuildShape(); + void Highlight(int highlightOn) override ; + double* GetWorldPosition(); + void SetWorldPosition(double x,double y, double z); + void SetWorldPosition(double* pos){ + SetWorldPosition(pos[0],pos[1],pos[2]); + } +protected: + ControlPointActor(); + ~ControlPointActor() override; + + vtkNew shadowData; + + +}; + + +#endif //OMEGAV_CONTROLPOINTACTOR_H diff --git a/src/include/measure/ControlPointRActor.h b/src/include/measure/ControlPointRActor.h new file mode 100644 index 0000000..c8339af --- /dev/null +++ b/src/include/measure/ControlPointRActor.h @@ -0,0 +1,31 @@ +// +// Created by 87714 on 2021/8/10. +// + +#ifndef OMEGAV_CONTROLPOINTRACTOR_H +#define OMEGAV_CONTROLPOINTRACTOR_H +#include "ControlPointActor.h" + +class ControlPointRActor: public ControlPointActor { +public: + //@{ + /** + * Standard methods for instances of this class. + */ + static ControlPointRActor* New(); + vtkTypeMacro(ControlPointRActor,ControlPointActor); + //@} + + void BuildShape(); + void Highlight(int highlightOn) override; + void MouseEntered() override { + this->InvokeEvent(vtkCommand::EnterEvent, nullptr); + } + void MouseLeave() override { + this->InvokeEvent(vtkCommand::LeaveEvent, nullptr); + } + vtkIdType Index; +}; + + +#endif //OMEGAV_CONTROLPOINTRACTOR_H diff --git a/src/include/measure/DraggableActor.h b/src/include/measure/DraggableActor.h new file mode 100644 index 0000000..886e71d --- /dev/null +++ b/src/include/measure/DraggableActor.h @@ -0,0 +1,161 @@ +// +// Created by 87714 on 2021/6/21. +// + +#ifndef OMEGAV_DRAGGABLEACTOR_H +#define OMEGAV_DRAGGABLEACTOR_H + +#include "vtkProp.h" +#include "vtkPointHandleRepresentation2D.h" +#include "vtkActor2D.h" +#include "vtkAxisActor2D.h" +#include "vtkPolyDataMapper2D.h" +#include "vtkActor2D.h" +#include "vtkCommand.h" +#include "functional" + +#include "vtkNew.h" // for ivars +#include "vtkPoints.h" +#include "vtkPointSet.h" +#include "Measure.h" + +class vtkPolyData; +class vtkPolyDataMapper2D; +class vtkRenderer; +class vtkTextActor; + +using DragCallBack = std::function; + +class DraggableActor : public vtkProp { +public: + //@{ + /** + * Standard methods for instances of this class. + */ + static DraggableActor *New(); + + vtkTypeMacro(DraggableActor, vtkProp); +// void PrintSelf(ostream& os, vtkIndent indent) override; + //@} + + enum DraggableActorEvents { + DragEvent = vtkCommand::UserEvent + 300, + DragEndEvent, + SelectedEvent, + UnSelectedEvent, + }; + + virtual void SetRenderer(vtkRenderer *ren); + + virtual vtkRenderer *GetRenderer() { + return Renderer; + }; + + virtual void BuildShape() {}; + + virtual bool Hit(int x, int y); + + virtual DraggableActor *GetDragItem() { + return this; + }; + + void Pick() override; + + virtual void MouseEntered() { + this->InvokeEvent(vtkCommand::EnterEvent, nullptr); + Highlight(1); + }; + + virtual void MouseLeave() { + Highlight(0); + this->InvokeEvent(vtkCommand::LeaveEvent, nullptr); + }; + + virtual void Highlight(int highlightOn); + vtkGetMacro(Highlighted,bool); + vtkSetMacro(Highlighted,bool); + + virtual void Select(int Selected); + vtkGetMacro(Selected,bool); + vtkSetMacro(Selected,bool); + void SelectedOn(){ + Select(1); + } + void SelectedOff(){ + Select(0); + } + virtual void RenderWithState(); + + //@{ + /** + * Methods to make this class behave as a vtkProp. + */ + double *GetBounds() VTK_SIZEHINT(6) override { return nullptr; } + + void GetActors(vtkPropCollection *) override {} + + void GetVolumes(vtkPropCollection *) override {} + + void ShallowCopy(vtkProp *prop) override {}; + + void ReleaseGraphicsResources(vtkWindow *) override; + + int RenderOverlay(vtkViewport *viewport) override; + //@} + + int RenderOpaqueGeometry(vtkViewport *vtkNotUsed(viewport)) override { return 0; } + + int RenderTranslucentPolygonalGeometry(vtkViewport *vtkNotUsed(viewport)) override { return 0; } + + int RenderVolumetricGeometry(vtkViewport *vtkNotUsed(viewport)) override { return 0; } + + vtkTypeBool HasTranslucentPolygonalGeometry() override { return 0; } + + virtual void Transform(float x, float y); + virtual void ApplyTransform(); + + void SetOnDrag(DragCallBack f) { + this->OnDrag = f; + } + + void SetOnDragEnd(DragCallBack f) { + this->OnDragEnd = f; + } + void Hide(); + void Show(); + +protected: + DraggableActor(); + ~DraggableActor() override; + + vtkPoints *BaseDataPoints; + vtkPoints *renderPoints; + vtkPoints *tempStorePoints = nullptr; + vtkNew renderData; + //for test + vtkActor2D * actor2D; + vtkActor2D * shadow2D; + vtkActor2D * senseArea; + vtkActor2D * text = nullptr; + vtkRenderer *Renderer; + + void GetSlicePlanePoint(double x, double y, vtkRenderer *renderer, double *result); + + bool transforming = false; + bool Highlighted = false; + bool Selected = false; + + void RebuildRenderPoint(); + +private: + DraggableActor(const DraggableActor &) = delete; + + void operator=(const DraggableActor &) = delete; + + DragCallBack OnDrag = nullptr; + + DragCallBack OnDragEnd = nullptr; +}; + + +#endif //OMEGAV_DRAGGABLEACTOR_H diff --git a/src/include/measure/EllipseAnnotationActor.h b/src/include/measure/EllipseAnnotationActor.h new file mode 100644 index 0000000..50b5cf6 --- /dev/null +++ b/src/include/measure/EllipseAnnotationActor.h @@ -0,0 +1,47 @@ +// +// Created by 87714 on 2021/6/22. +// + +#ifndef OMEGAV_EllipseANNOTATIONACTOR_H +#define OMEGAV_EllipseANNOTATIONACTOR_H + +#include "DraggableActor.h" +#include "Measure.h" +class vtkTextProperty; +class ControlPointActor; +class EllipseAnnotationActor: public DraggableActor, public Measure { +public: + //@{ + /** + * Standard methods for instances of this class. + */ + static EllipseAnnotationActor* New(); + vtkTypeMacro(EllipseAnnotationActor,DraggableActor); + //@} + void controlPointsTransform(float x,float y); + void controlPointsApplyTransform(); + void SetRenderer(vtkRenderer* ren); + void BuildShape() override; + void Transform(float x, float y) override; + void ApplyTransform() override; + bool onMeasureLeftButtonDown(vtkRenderWindowInteractor*) override; + void onMeasureMouseMove(vtkRenderWindowInteractor*) override; + bool onMeasureLeftButtonUp(vtkRenderWindowInteractor*) override; + NextMeasureMacro(EllipseAnnotationActor); +protected: + EllipseAnnotationActor(); + ~EllipseAnnotationActor() override; + +private: + ControlPointActor* controlP_rt= nullptr; + ControlPointActor* controlP_lb = nullptr; + ControlPointActor* controlP_rb = nullptr; + ControlPointActor* controlP_lt= nullptr; + void selfDragCb(vtkObject*, unsigned long event, void* data); + void controlPointCb(vtkObject * sender, unsigned long event, void *data); + void drawCircle(double *p1,double *p2); + vtkTextProperty* textProperty; +}; + + +#endif //OMEGAV_EllipseANNOTATIONACTOR_H diff --git a/src/include/measure/EventsCenter.h b/src/include/measure/EventsCenter.h new file mode 100644 index 0000000..b860b98 --- /dev/null +++ b/src/include/measure/EventsCenter.h @@ -0,0 +1,66 @@ +// +// Created by 87714 on 2021/6/23. +// + +#ifndef OMEGAV_EVENTSCENTER_H +#define OMEGAV_EVENTSCENTER_H + +#include +#include "QtCore/qmap.h" +#include "QString" +#define aLLEventMacro() \ + _add_Event(DefaultEvent)\ + _add_Event(DragEvent) + + +enum Events{ + #define _add_Event(enum) enum, + aLLEventMacro() + #undef _add_Event +}; +class EventObject { +public: + void* Sender= nullptr; + bool Cancel = false; + QMap Detail; + void AddDetailValue(const QString& key, void* val) + { + Detail[key]=val; + } +}; +class EventsCenter:public QObject { + Q_OBJECT +public: + static EventsCenter* Instance(){ + return instance; + } + static void TriggerEvent(Events eventId, EventObject &callData){ + Instance()->triggerEvent(eventId, callData); + } +signals: + #define _add_Event(enum) void enum (EventObject &); + aLLEventMacro() + #undef _add_Event +private: + void triggerEvent(Events eventId, EventObject &callData){ + switch (eventId) { + #define _add_Event(enum) \ + case Events::enum:\ + {\ + emit enum(callData);\ + break;\ + }; + + aLLEventMacro() + #undef _add_Event + default: + emit DefaultEvent(callData); + break; + } + } + static EventsCenter* instance; + +}; + + +#endif //OMEGAV_EVENTSCENTER_H diff --git a/src/include/measure/HoverButton.h b/src/include/measure/HoverButton.h new file mode 100644 index 0000000..a62731f --- /dev/null +++ b/src/include/measure/HoverButton.h @@ -0,0 +1,24 @@ +// +// Created by 87714 on 2021/7/22. +// + +#ifndef OMEGAV_HOVERBUTTON_H +#define OMEGAV_HOVERBUTTON_H + +#include +class HoverButton:public QPushButton { + Q_OBJECT +public: + explicit HoverButton(QWidget *parent = Q_NULLPTR); + ~HoverButton(); + signals: + void hovered(); + void endHover(); + +protected: + void enterEvent(QEvent *) override; + void leaveEvent(QEvent *) override ; +}; + + +#endif //OMEGAV_HOVERBUTTON_H diff --git a/src/include/measure/KDicomObjects.h b/src/include/measure/KDicomObjects.h new file mode 100644 index 0000000..3a6e448 --- /dev/null +++ b/src/include/measure/KDicomObjects.h @@ -0,0 +1,87 @@ +// +// Created by 87714 on 2021/7/25. +// + +#ifndef OMEGAV_KDICOMOBJECTS_H +#define OMEGAV_KDICOMOBJECTS_H + + +#include +#include +#include + + +using namespace std; +class KDicomPatient; +class KDicomStudy; +class KDicomSeries; +class KDicomImage{ +public: + string SOPInstanceUID; + string RefFilePath; + int ImageIndex = 0; + signed long InstanceNumber = 0; + double WindowWidth = 255.0; + double WindowCenter = 0.0; + double PatientImagePosition[3] = {0.0,0.0,0.0}; + double PatientImageOrientation[6] = {0.0,0.0,0.0,0.0,0.0,0.0}; + double PixelSpacing[2] = {1.0,1.0}; + double SliceThickness = 1.0; + unsigned short Rows = 0; + unsigned short Columns = 0; + KDicomSeries* Series = nullptr; + ~KDicomImage(){ + Series = nullptr; + } +}; +class KDicomSeries +{ +public: + string StudyUID; + string seriesUID; + string SeriesDescription; + string AccessionNumber; + signed long SeriesNumber = 0; + signed long AcquisitionNumber = 0; + KDicomStudy* Study; + vector> Images; + ~KDicomSeries(){ + Study = nullptr; + } +}; +class KDicomStudy +{ +public: + string StudyDate; + string StudyDescription; + string InstitutionName; + string StudyUID; + string StudyID; + string Modality; + KDicomPatient* Patient; + + unordered_map> Series; + ~KDicomStudy(){ + Patient = nullptr; + } +}; +class KDicomPatient{ +public: + string PatientName; + string PatientID; + string PatientSex; + string PatientBirthDate; + unordered_map> Studys; +}; +class KDicomRoot{ +public: + static KDicomRoot* Instance(){ + static KDicomRoot root; + return &root; + } + unordered_map> Patients; + void LoadFromDir(const char * path); +}; + + +#endif //OMEGAV_KDICOMOBJECTS_H diff --git a/src/include/measure/LineAnnotationActor.h b/src/include/measure/LineAnnotationActor.h new file mode 100644 index 0000000..2666415 --- /dev/null +++ b/src/include/measure/LineAnnotationActor.h @@ -0,0 +1,51 @@ +// +// Created by 87714 on 2021/6/22. +// + +#ifndef OMEGAV_LINEANNOTATIONACTOR_H +#define OMEGAV_LINEANNOTATIONACTOR_H +#include "DraggableActor.h" +#include "Measure.h" +class ControlPointActor; +class LineAnnotationActor: public DraggableActor, public Measure { +public: + //@{ + /** + * Standard methods for instances of this class. + */ + static LineAnnotationActor* New(); + vtkTypeMacro(LineAnnotationActor,DraggableActor); + //@} + void SetRenderer(vtkRenderer* ren); + void BuildShape() override; + void SetWorldPosition1(double x,double y, double z); + void SetWorldPosition1(double* pos){ + SetWorldPosition1(pos[0],pos[1],pos[2]); + } + void SetWorldPosition2(double x,double y, double z); + void SetWorldPosition2(double* pos){ + SetWorldPosition2(pos[0],pos[1],pos[2]); + } + + bool onMeasureLeftButtonDown(vtkRenderWindowInteractor*) override; + void onMeasureMouseMove(vtkRenderWindowInteractor*) override; + bool onMeasureLeftButtonUp(vtkRenderWindowInteractor*) override; + NextMeasureMacro(LineAnnotationActor); + void ForceDelete() override { + this->SetRenderer(nullptr); +#ifdef _DEBUG + printf("LineAnnotationActor delete \r\n"); +#endif + DraggableActor::Delete(); + } +protected: + LineAnnotationActor(); + ~LineAnnotationActor() override; + ControlPointActor* controlP1= nullptr; + ControlPointActor* controlP2= nullptr; + void selfDragCb(vtkObject*, unsigned long event, void* data); + void controlPointCb(vtkObject * sender, unsigned long event, void *data); +}; + + +#endif //OMEGAV_LINEANNOTATIONACTOR_H diff --git a/src/include/measure/Measure.h b/src/include/measure/Measure.h new file mode 100644 index 0000000..6c30068 --- /dev/null +++ b/src/include/measure/Measure.h @@ -0,0 +1,63 @@ + +#ifndef OMEGAV_MEASURE_H +#define OMEGAV_MEASURE_H + +#define NextMeasureMacro(newMeasure) \ +Measure* GetNextMeasure() override\ +{\ + return newMeasure::New();\ +}\ + + +#include +#include "vtkRenderWindowInteractor.h" +class Measure{ +public: + virtual bool onMeasureDoubleClick(vtkRenderWindowInteractor*){ return this->Placing;}; + //Measure left bond + virtual bool onMeasureLeftButtonDown(vtkRenderWindowInteractor*){ return false;}; + //Mouse movement handle during measurement + virtual void onMeasureMouseMove(vtkRenderWindowInteractor*){}; + //Left-click up, and the returned value indicates whether the measurement setting operation continues. + virtual bool onMeasureLeftButtonUp(vtkRenderWindowInteractor*){return false;}; + //not used + virtual void onTerminate(vtkRenderWindowInteractor*){}; + bool isMeasurePlacing(){ + return this->Placing; + } + void SetPlacing(bool p){ + if (Hidden) + { + Hidden = false; + } + this->Placing=p; + } + + void SetMeasureID(const QString &Id){ + id = Id; + } + + QString GetMeasureID(){ + return id; + } + + static void SetHidden(bool h){ + Measure::Hidden=h; + } + + static bool GetHidden(){ + return Measure::Hidden; + } + + virtual Measure* GetNextMeasure(){ return nullptr;} + virtual void ForceDelete(){}; + virtual bool Valid(){return true;} + +protected: + static bool Hidden; +private: + bool Placing = false; + QString id = ""; +}; + +#endif //OMEGAV_MEASURE_H diff --git a/src/include/measure/MeasureStore.h b/src/include/measure/MeasureStore.h new file mode 100644 index 0000000..c7d9c1b --- /dev/null +++ b/src/include/measure/MeasureStore.h @@ -0,0 +1,34 @@ +// +// Created by 87714 on 2021/7/19. +// + +#ifndef OMEGAV_MEASURESTORE_H +#define OMEGAV_MEASURESTORE_H +class DraggableActor; +class Measure; +#include "unordered_map" +#include "QMap" +#include "QList" +class MeasureStore { +public: + static MeasureStore* Instance(){ + if (instance == nullptr) + { + instance = new MeasureStore(); + } + return instance; + } + void Store(QString SeriesUid,int slice,Measure* measure); + void Remove(Measure* measure); + void RemoveAllInSlice(QString SeriesUid,int slice); + void RemoveAllInSeries(QString SeriesUid); + void Clear(); + QList* GetMeasures(QString SeriesUid, int slice); + +private: + QMap>> store; + static MeasureStore* instance; +}; + + +#endif //OMEGAV_MEASURESTORE_H diff --git a/src/include/measure/OpenPolyAnnotationActor.h b/src/include/measure/OpenPolyAnnotationActor.h new file mode 100644 index 0000000..5c9a55e --- /dev/null +++ b/src/include/measure/OpenPolyAnnotationActor.h @@ -0,0 +1,56 @@ +// +// Created by 87714 on 2021/8/13. +// + +#ifndef OMEGAV_OPENPOLYANNOTATIONACTOR_H +#define OMEGAV_OPENPOLYANNOTATIONACTOR_H + +#include "DraggableActor.h" +#include "Measure.h" +class ControlPointRActor; +class OpenPolyAnnotationActor: public DraggableActor, public Measure { +public: + //@{ + /** + * Standard methods for instances of this class. + */ + static OpenPolyAnnotationActor* New(); + vtkTypeMacro(OpenPolyAnnotationActor,DraggableActor); + //@} + void SetRenderer(vtkRenderer* ren) override; + void BuildShape() override; + void SetPointWorldPosition(vtkIdType index, double x, double y, double z); + + void Highlight(int f) override ; + bool onMeasureDoubleClick(vtkRenderWindowInteractor*) override; + bool onMeasureLeftButtonDown(vtkRenderWindowInteractor*) override; + void onMeasureMouseMove(vtkRenderWindowInteractor*) override; + bool onMeasureLeftButtonUp(vtkRenderWindowInteractor*) override; + NextMeasureMacro(OpenPolyAnnotationActor); + void ForceDelete() override { + this->SetRenderer(nullptr); + printf("OpenPolyAnnotationActor delete \r\n"); + Delete(); + } + vtkSetMacro(Closed, vtkTypeBool); + vtkGetMacro(Closed, vtkTypeBool); + vtkBooleanMacro(Closed, vtkTypeBool); + bool Valid() override { + return Closed==0?BaseDataPoints->GetNumberOfPoints()>1:BaseDataPoints->GetNumberOfPoints()>2; + } +protected: + OpenPolyAnnotationActor(); + ~OpenPolyAnnotationActor() override; + void HighlightOn(){ + Highlight(1); + } + void HighlightOff(){ + Highlight(0); + } + std::vector controlPointList; + vtkTypeBool Closed = 0; + void selfDragCb(vtkObject*, unsigned long event, void* data); + void controlPointCb(vtkObject * sender, unsigned long event, void *data); +}; + +#endif //OMEGAV_OPENPOLYANNOTATIONACTOR_H diff --git a/src/include/measure/QDicomUitility.h b/src/include/measure/QDicomUitility.h new file mode 100644 index 0000000..980c6e2 --- /dev/null +++ b/src/include/measure/QDicomUitility.h @@ -0,0 +1,56 @@ +// +// Created by 87714 on 2021/7/26. +// + +#ifndef OMEGAV_QDICOMUITILITY_H +#define OMEGAV_QDICOMUITILITY_H + + +#include +#include +#include +#include +#include +#include +#include +#include + +QPixmap GetImageFormDcmFile(const QString& filepath, int wl, int ww) +{ + DcmRLEDecoderRegistration::registerCodecs(OFFalse, OFTrue);//娉ㄥ唽瑙g爜鍣 + /// register JPEG decompression codecs + DJDecoderRegistration::registerCodecs();//娉ㄥ唽瑙g爜鍣 + DJLSDecoderRegistration::registerCodecs();// +// FMJPEG2KDecoderRegistration::registerCodecs();//jpeg2000 + DcmFileFormat *fileFormat = new DcmFileFormat();//璇诲彇鏂囦欢鑾峰彇浼犺緭璇硶 + QPixmap pixmap(100,100); + pixmap.fill(Qt::black); + if (fileFormat->loadFile(filepath.toLatin1().data()).good()) + { + DcmDataset * dset = fileFormat->getDataset(); + + //灏嗗師濮嬩紶杈撹娉曡緭鍏icomImage缂栬瘧绫诲簱鑷垜瑙e帇鍘嬬缉鐨勫浘鍍 + DicomImage dcmImage(fileFormat, dset->getOriginalXfer(),CIF_MayDetachPixelData); + unsigned long w = 100; + bool flag = dcmImage.getWidth() > dcmImage.getHeight(); + DicomImage* sdcmImage = dcmImage.createScaledImage(w,0,0,1); + sdcmImage->setWindow(wl, ww); + uchar* data = (uchar*)sdcmImage->getOutputData(8);//鎸8浣嶇殑浣嶅鍙栨暟鎹 + unsigned long size = sdcmImage->getOutputDataSize(8); + QImage image(data, sdcmImage->getWidth(), sdcmImage->getHeight(), QImage::Format_Grayscale8);//浣跨敤8浣嶆繁搴︾殑鐏板害鍥惧仛杈撳嚭 + if (sdcmImage->getHeight() > 100) image = image.scaledToHeight(100); + QPainter p(&pixmap); + p.drawPixmap(50 - image.width()/2, 50-image.height()/2, QPixmap::fromImage(image)); + + // p.save(); + } + delete fileFormat; + DcmRLEDecoderRegistration::cleanup(); + DJDecoderRegistration::cleanup(); + DJLSDecoderRegistration::cleanup(); +// FMJPEG2KDecoderRegistration::cleanup(); + return pixmap; +} + + +#endif //OMEGAV_QDICOMUITILITY_H diff --git a/src/include/measure/RulerAnnotationActor.h b/src/include/measure/RulerAnnotationActor.h new file mode 100644 index 0000000..b520434 --- /dev/null +++ b/src/include/measure/RulerAnnotationActor.h @@ -0,0 +1,47 @@ +// +// Created by 87714 on 2021/7/12. +// + +#ifndef OMEGAV_RULERANNOTATIONACTOR_H +#define OMEGAV_RULERANNOTATIONACTOR_H + + +#include "LineAnnotationActor.h" + +class RulerAnnotationActor : public LineAnnotationActor { +public: + static RulerAnnotationActor* New(); + vtkTypeMacro(RulerAnnotationActor,LineAnnotationActor); + void BuildShape() override; + NextMeasureMacro(RulerAnnotationActor); + + void setCalibration(double s) + { + isCalibration = true; + calib_dis = s; + } + double getCalibration() { + if (isCalibration) + { + return calib_dis; + } + else + { + return real_dis; + } + } + +protected: + RulerAnnotationActor(); + ~RulerAnnotationActor() override; + +private: + void selfCalibCb(vtkObject *, unsigned long, void *data); + vtkTextProperty* textProperty; + double real_dis; + double calib_dis; + bool isCalibration = false; +}; + + +#endif //OMEGAV_RULERANNOTATIONACTOR_H diff --git a/src/include/measure/RulerLegendActor.h b/src/include/measure/RulerLegendActor.h new file mode 100644 index 0000000..9b073e1 --- /dev/null +++ b/src/include/measure/RulerLegendActor.h @@ -0,0 +1,67 @@ +// +// Created by 87714 on 2021/9/2. +// + +#ifndef OMEGAV_RULERLEGENDACTOR_H +#define OMEGAV_RULERLEGENDACTOR_H + +#include "vtkProp.h" +#include "vtkActor2D.h" +#include "vtkPolyData.h" +#include "vtkNew.h" // for ivars + +class RulerLegendActor : public vtkProp { +public: + //@{ + /** + * Standard methods for instances of this class. + */ + static RulerLegendActor *New(); + + vtkTypeMacro(RulerLegendActor, vtkProp); + + virtual void BuildShape(int* size, double scale); + + //@{ + /** + * Methods to make this class behave as a vtkProp. + */ + double *GetBounds() VTK_SIZEHINT(6) override { return nullptr; } + + void GetActors(vtkPropCollection *) override {} + + void GetVolumes(vtkPropCollection *) override {} + + void ShallowCopy(vtkProp *prop) override {}; + + void ReleaseGraphicsResources(vtkWindow *) override; + + /** + * Method use to make this actor render in 2D scene; + * @param viewport + * @return render is success + */ + int RenderOverlay(vtkViewport *viewport) override; + + int RenderOpaqueGeometry(vtkViewport *vtkNotUsed(viewport)) override { return 0; } + + int RenderTranslucentPolygonalGeometry(vtkViewport *vtkNotUsed(viewport)) override { return 0; } + + int RenderVolumetricGeometry(vtkViewport *vtkNotUsed(viewport)) override { return 0; } + + vtkTypeBool HasTranslucentPolygonalGeometry() override { return 0; } + + +protected: + RulerLegendActor(); + ~RulerLegendActor() override; + + vtkNew renderData; + vtkActor2D * actor2D; + +private: + RulerLegendActor(const RulerLegendActor &) = delete; + void operator=(const RulerLegendActor &) = delete; +}; + +#endif //OMEGAV_RULERLEGENDACTOR_H diff --git a/src/include/measure/SeriesItem.h b/src/include/measure/SeriesItem.h new file mode 100644 index 0000000..016fc7f --- /dev/null +++ b/src/include/measure/SeriesItem.h @@ -0,0 +1,25 @@ +// +// Created by 87714 on 2021/7/23. +// + +#ifndef OMEGAV_SERIESITEM_H +#define OMEGAV_SERIESITEM_H + +#include +#include +#include +class SeriesItem:public QWidget { +Q_OBJECT +public: + explicit SeriesItem(QWidget *parent = Q_NULLPTR); + ~SeriesItem() override; + void SetStudy(QString studyUid,QString studyDate, QString Modality); + void AddSeries(QString seriesUid,QString des,int count); +private: + QVBoxLayout mainLayout; + QPushButton studyHeader; + int series_count=0; +}; + + +#endif //OMEGAV_SERIESITEM_H diff --git a/src/include/measure/StudyItem.h b/src/include/measure/StudyItem.h new file mode 100644 index 0000000..38449a8 --- /dev/null +++ b/src/include/measure/StudyItem.h @@ -0,0 +1,32 @@ +// +// Created by 87714 on 2021/7/23. +// + +#ifndef OMEGAV_STUDYITEM_H +#define OMEGAV_STUDYITEM_H + +#include +#include +#include +class StudyItem: public QWidget { +Q_OBJECT +public: + explicit StudyItem(QWidget *parent = Q_NULLPTR); + ~StudyItem() override; + void setPatientID(QString patientId){ + this->patientID=patientId; + } + void SetStudy(QString studyUid,QString studyDate, QString studyDes,QString Modality); + void AddSeries(QString seriesUid,QString des,QPixmap img); + signals: + void SeriesButtonClick(QString patientID,QString studyUid,QString seriesUid); +private: + QVBoxLayout mainLayout; + QPushButton studyHeader; + QString patientID; + QString studyUid; + int series_count=0; +}; + + +#endif //OMEGAV_STUDYITEM_H diff --git a/src/include/measure/TextAnnotationActor.h b/src/include/measure/TextAnnotationActor.h new file mode 100644 index 0000000..67e6277 --- /dev/null +++ b/src/include/measure/TextAnnotationActor.h @@ -0,0 +1,61 @@ +// +// Created by 87714 on 2021/6/22. +// + +#ifndef OMEGAV_TEXTANNOTATIONACTOR_H +#define OMEGAV_TEXTANNOTATIONACTOR_H +#include "DraggableActor.h" +#include "Measure.h" +#include "vtkTextMapper.h" +#include "vtkTextProperty.h" +class ControlPointActor; +//class pqFontPropertyWidget; +class TextAnnotationActor : public DraggableActor, public Measure { +public: + //@{ + /** + * Standard methods for instances of this class. + */ + static TextAnnotationActor* New(); + vtkTypeMacro(TextAnnotationActor, DraggableActor); + //@} + void SetRenderer(vtkRenderer* ren); + + vtkTextProperty* GetTextProp() + { + return this->textProperty; + } + char * GetTextInput() + { + return m_TextStr; + } + void SetTextInput(const char* str); + + void ResetTextProp(); + void BuildShape() override; + void SetWorldPosition1(double x, double y, double z); + void SetWorldPosition1(double* pos) { + SetWorldPosition1(pos[0], pos[1], pos[2]); + } + void SetWorldPosition2(double x, double y, double z); + void SetWorldPosition2(double* pos) { + SetWorldPosition2(pos[0], pos[1], pos[2]); + } + bool onMeasureLeftButtonDown(vtkRenderWindowInteractor*) override; + void onMeasureMouseMove(vtkRenderWindowInteractor*) override; + bool onMeasureLeftButtonUp(vtkRenderWindowInteractor*) override; + NextMeasureMacro(TextAnnotationActor); +protected: + TextAnnotationActor(); + ~TextAnnotationActor() override; + +private: + void selfDragCb(vtkObject*, unsigned long event, void* data); + void selfPickCb(vtkObject*, unsigned long event, void* data); + vtkTextProperty* textProperty; + vtkRenderWindowInteractor *m_iren; + char m_TextStr[100]; +}; + + +#endif //OMEGAV_TEXTANNOTATIONACTOR_H diff --git a/src/include/measure/calibrationWidget.h b/src/include/measure/calibrationWidget.h new file mode 100644 index 0000000..6dbb7b7 --- /dev/null +++ b/src/include/measure/calibrationWidget.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include "ui_calibrationWidget.h" +#include "RulerAnnotationActor.h" + +namespace Ui { + class calibrationWidget; +} + +class calibrationWidget : public QDialog +{ + Q_OBJECT +public: + explicit calibrationWidget(RulerAnnotationActor *rulerActor,vtkRenderWindowInteractor* iren); + ~calibrationWidget(); +public slots: + void SetTextInput(); +private: + Ui::calibrationWidget ui; + RulerAnnotationActor* _rulerActor; + vtkRenderWindowInteractor* _rulerRender; +}; \ No newline at end of file diff --git a/src/include/measure/pqFontPropertyWidget.h b/src/include/measure/pqFontPropertyWidget.h new file mode 100644 index 0000000..55e0f75 --- /dev/null +++ b/src/include/measure/pqFontPropertyWidget.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include "ui_pqFontPropertyWidget.h" +#include "TextAnnotationActor.h" + +namespace Ui { + class FontPropertyWidget; +} + + +//class DicomImageView; +class pqFontPropertyWidget : public QDialog +{ + Q_OBJECT +public: + static pqFontPropertyWidget* GetPropDlg(); + void SetTextActor(TextAnnotationActor*); + void SetRenderWindowInteractor(vtkRenderWindowInteractor* ren); + + //void SetViewer(DicomImageView*); + void reloadProperties(); +public slots: + void SetFontSize(int); + void SetOpacity(double); + void SetFontFamily(int); + void BoldOn(bool); + void ItalicOn(bool); + void ShadowOn(bool); + //void ColorEditor(); + void ColorChooser(); + void SetTextInput(); + void ResetText(); + +private: + static pqFontPropertyWidget *propDlg; + explicit pqFontPropertyWidget(QWidget *parent = Q_NULLPTR); + ~pqFontPropertyWidget(); + Ui::FontPropertyWidget ui; + TextAnnotationActor* textActor; + vtkRenderWindowInteractor* tRender; + //DicomImageView* viewer; +}; diff --git a/src/include/measure/vtkArrow2DSource.h b/src/include/measure/vtkArrow2DSource.h new file mode 100644 index 0000000..4f729c1 --- /dev/null +++ b/src/include/measure/vtkArrow2DSource.h @@ -0,0 +1,39 @@ +// +// Created by 87714 on 2021/8/11. +// + +#ifndef OMEGAV_VTKARROW2DSOURCE_H +#define OMEGAV_VTKARROW2DSOURCE_H + +#include "vtkPolyLineSource.h" + +class vtkArrow2DSource : public vtkPolyLineSource +{ +public: + /** + * Construct cone with angle of 45 degrees. + */ + static vtkArrow2DSource *New(); + + vtkTypeMacro(vtkArrow2DSource,vtkPolyDataAlgorithm); + void PrintSelf(ostream& os, vtkIndent indent) override; + /** + * Set the length, of the tip. It default to 10.0 + */ + vtkSetClampMacro(TipLength,double,0.0,1.0); + vtkGetMacro(TipLength,double); + //@} +protected: + vtkArrow2DSource(); + ~vtkArrow2DSource() override; + + int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector *) override; + +private: + vtkArrow2DSource(const vtkArrow2DSource&) = delete; + void operator=(const vtkArrow2DSource&) = delete; + double TipLength = 10.0; +}; + + +#endif //OMEGAV_VTKARROW2DSOURCE_H diff --git a/src/include/measure/vtkSignalRaiser.h b/src/include/measure/vtkSignalRaiser.h new file mode 100644 index 0000000..4136569 --- /dev/null +++ b/src/include/measure/vtkSignalRaiser.h @@ -0,0 +1,23 @@ +// +// Created by 87714 on 2021/7/23. +// + +#ifndef OMEGAV_VTKSIGNALRAISER_H +#define OMEGAV_VTKSIGNALRAISER_H + +#include +#include +class vtkSignalRaiser: public QObject { + Q_OBJECT +public: + signals: + void raiseEvent(vtkObject* sender,unsigned long eventId,void* callData = nullptr); + +public : + void raise(vtkObject* sender,unsigned long eventId,void* callData = nullptr){ + emit raiseEvent(sender,eventId,callData); + } +}; + + +#endif //OMEGAV_VTKSIGNALRAISER_H diff --git a/src/include/util/ColorMapReader.h b/src/include/util/ColorMapReader.h new file mode 100644 index 0000000..2f3084a --- /dev/null +++ b/src/include/util/ColorMapReader.h @@ -0,0 +1,55 @@ +// +// Created by 87714 on 2021/9/4. +// + +#ifndef OMEGAV_COLORMAPREADER_H +#define OMEGAV_COLORMAPREADER_H + +#include +#include +#include +#include +#include "QGlobals.h" + +using namespace std; +class ColorMapReader { +public: + ColorMapReader() = default; + //ColorMapReader(){ + // //ParseFile(path); + //} + bool ParseFile(const char* path); + vector& GetPresetNames() { + printf("%x\r\n", &names); + return names; + } + enum PresetType { + Undefined, + RGBPoints, + IndexedColors + }; + PresetType GetPresetType(const char* name); + const vector& GetPresetValues(const char* name); + double* GetPresetNanColor(const char* name); + static ColorMapReader* DefaultPresets() { + static ColorMapReader defaultInstance; + //static ColorMapReader defaultInstance(":/Config/Json/ColorMaps.json"); + if (defaultInstance.ParseFile(COLOR_MAP_PATH.c_str())) + { + return &defaultInstance; + } + else + { + return nullptr; + } + } +private: + //vector Names; + QJsonDocument doc; + vector names; + vector valStore; + unordered_map name_index_map; +}; + + +#endif //OMEGAV_COLORMAPREADER_H diff --git a/src/include/view/dicomimageview.h b/src/include/view/dicomimageview.h new file mode 100644 index 0000000..4941d1d --- /dev/null +++ b/src/include/view/dicomimageview.h @@ -0,0 +1,233 @@ +锘#pragma once +#include +#include +#include +#include +#include "base/SeriesInstance.h" +#include "view/myQVTKOpenGLNativeWidget.h" + +#include +class infinitiViewer; +class vtkCornerAnnotation; +class vtkGenericOpenGLRenderWindow; +class MyTitleBar; +class pqVCRController; +class pqVCRToolbar; + + +namespace scrollScope { + enum TriggerType + { + USER_TRIGGER, //a,b,c + STYLE_TRIGGER,//b,c + SYNC_ONLY,//do nothing + }; +} +class DicomImageView : public QFrame +{ + Q_OBJECT + typedef QOpenGLWidget Superclass; +public: + //a:real effect + //b:corner info update + //c:scroll value sync + explicit DicomImageView(QWidget* parent = Q_NULLPTR); + ~DicomImageView() override; + + + void ShowMetaData(); + myQVTKOpenGLNativeWidget* getGLWidget() + { + return _glWidt; + } + vtkGenericOpenGLRenderWindow* getRenWin() + { + return m_glrenWin; + } + infinitiViewer* getImageViewer() + { + return _ImageViewer; + } + + void setScrollChangedType(int type) + { + _ScrollTriggerType = type; + } + void setHighlight(bool yes); + void Render(); + + //Series + void setDicomImageView(SeriesInstance *series); + bool HasSeries(); + int getSeriesNumber(); + SeriesInstance* getSeriesInstance() + { + return _Series; + } + + //Reset + void ResetView(); + + + //Corner Info + void updateCornerInfoAll(); + void updateCornerInfoPrivacy(); + + //Window level + void getWindowLevel(double &level, double &width); + void setWindowLevel(double level, double width); + + //Transformation + void ClearTransformations(); + void HFlip(); + void VFlip(); + void Rotate(double angle, TransFormType operation); + + + //Fusion + bool IsFusion(); + void IncreFusionOpacity(double percent); + void SetFusionInput(DicomImageView *overlap); + void removeViewWithFusion(); + + + //Measure + void ActiveMeasure(Measure *m); + void DeleteSelectedMeasure(); + void DeleteCurrentSliceMeasure(); + void DeleteCurrentSeriesMeasure(); + void removeViewWithMeasure(); + + + //Sync slice + void AddSlice(int step); + void SetSlice(int slice); + void SetZoomScale(double scale); + void SetPanOffset(double * p); + + //CineMode + void onFirstFrame(); + void onPreviousFrame(); + void onNextFrame(); + void onLastFrame(); + bool IsCine() { return isCine; } + void cineModeOn(); + bool isVCRVisible(); + void setVCRVisible(bool); + void setVCRToolbar(pqVCRToolbar* toolbar) + { + _vcr_toolbar = toolbar; + } + pqVCRToolbar* getVCRToolbar() + { + return _vcr_toolbar; + } + pqVCRController* getVCRController() + { + return _vcr_ctrl; + } + + //Negative + void ToggleNegativeMode(); + + +signals: + /** + * This signal will be emitted whenever a mouse event occurs within the QVTK window. + */ + + void scroll_ValueChanged(int); + void Signal_ViewEmpty(DicomImageView *view); + void Signal_ViewClicked(DicomImageView *view); + void Signal_viewDoubleclicked(DicomImageView *view); + void Signal_scrollValueChanged(DicomImageView *view, int slice); + void Signal_DragDropEvent(DicomImageView *view,thumbnailImage* tb); + void Signal_SyncEvent(DicomImageView *view, int interactionMode,void* calldata); + void Signal_WindowLevelEventForFusion(double level,double width); + void Signal_Transformation(TransFormType); + +public slots: + //for title bar use + void Slot_viewDoubleclicked(); + void Slot_ViewEmpty(); + void Slot_scrollValueChanged(int); + void Slot_WindowLevelEventForFusion(double level, double width); + void Slot_UpdateOrienInfo(TransFormType); + +protected: + void mousePressEvent(QMouseEvent* event) Q_DECL_OVERRIDE; + void mouseMoveEvent(QMouseEvent* event) Q_DECL_OVERRIDE; + void mouseReleaseEvent(QMouseEvent* event) Q_DECL_OVERRIDE; + void mouseDoubleClickEvent(QMouseEvent* event) Q_DECL_OVERRIDE; + void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; + + void dragEnterEvent(QDragEnterEvent *e); + void dragMoveEvent(QDragMoveEvent *e); + void dropEvent(QDropEvent *e); + void dragLeaveEvent(QDragLeaveEvent *e); + void resizeEvent(QResizeEvent *event); + + +private: + void removeFusion(); + + void OverlayOn() + { + isOverlay = true; + } + void OverlayOff() + { + isOverlay = false; + } + bool IsOverlay() + { + return isOverlay; + } + void SetBaseView(DicomImageView *v) + { + _base = v; + } + void SetOverlayView(DicomImageView *v) + { + _overlay = v; + } + + + + //Callback + void doubleclickedEventCb(vtkObject* sender, unsigned long eventId, void* calldata = nullptr); + void scalarEventCb(vtkObject* sender, unsigned long eventId, void* calldata = nullptr); + void updateWindowLevelCb(vtkObject*caller, unsigned long eid, void *calldata); + void syncEventFunc(vtkObject*caller, unsigned long eid, void *calldata); + + + void initCineModeThread(); + MyTitleBar* createMyTitleBar(); + void orphanizeSeriesInstance(); + void ResetPanZoom(); + + //bool deleteOlderSeries(SeriesInstance* old); + void initScrollbar(); + void CopyFromSeries(SeriesInstance *series); + + vtkSmartPointer m_glrenWin; + + infinitiViewer* _ImageViewer = nullptr; + SeriesInstance* _Series =nullptr; + QScrollBar* _scrollBar =nullptr; + MyTitleBar *_titleBar =nullptr; + myQVTKOpenGLNativeWidget* _glWidt = nullptr; + pqVCRToolbar* _vcr_toolbar = nullptr; + pqVCRController *_vcr_ctrl = nullptr; + QThread _thread; + DicomImageView *_overlay = nullptr; + DicomImageView *_base = nullptr; + + int _SliceStep =0; + int _PrevSlice =0; + int _ScrollTriggerType = scrollScope::TriggerType::USER_TRIGGER; + + bool isCine = false; + bool isNegative = false; + bool isOverlay = false; +}; diff --git a/src/include/view/myQVTKOpenGLNativeWidget.h b/src/include/view/myQVTKOpenGLNativeWidget.h new file mode 100644 index 0000000..455f062 --- /dev/null +++ b/src/include/view/myQVTKOpenGLNativeWidget.h @@ -0,0 +1,289 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: myQVTKOpenGLNativeWidget.h + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +/** + * @class myQVTKOpenGLNativeWidget + * @brief QOpenGLWidget subclass to house a vtkGenericOpenGLRenderWindow in a Qt + * application. + * + * myQVTKOpenGLNativeWidget extends QOpenGLWidget to make it work with a + * vtkGenericOpenGLRenderWindow. This is akin to QVTKWidget except it uses Qt to create and + * manage the OpenGL context using QOpenGLWidget (added in Qt 5.4). + * + * While myQVTKOpenGLNativeWidget is intended to be a replacement for QVTKWidget when + * using Qt 5, there are a few difference between myQVTKOpenGLNativeWidget and + * QVTKWidget. + * + * Unlike QVTKWidget, myQVTKOpenGLNativeWidget only works with vtkGenericOpenGLRenderWindow. + * This is necessary since QOpenGLWidget wants to take over the window management as + * well as the OpenGL context creation. Getting that to work reliably with + * vtkXRenderWindow or vtkWin32RenderWindow (and other platform specific + * vtkRenderWindow subclasses) was tricky and fraught with issues. + * + * Since myQVTKOpenGLNativeWidget uses QOpenGLWidget to create the OpenGL context, + * it uses QSurfaceFormat (set using `QOpenGLWidget::setFormat` or + * `QSurfaceFormat::setDefaultFormat`) to create appropriate window and context. + * You can use `myQVTKOpenGLNativeWidget::copyToFormat` to obtain a QSurfaceFormat + * appropriate for a vtkRenderWindow. + * + * A typical usage for myQVTKOpenGLNativeWidget is as follows: + * @code{.cpp} + * + * // before initializing QApplication, set the default surface format. + * QSurfaceFormat::setDefaultFormat(myQVTKOpenGLNativeWidget::defaultFormat()); + * + * vtkNew window; + * QPointer widget = new myQVTKOpenGLNativeWidget(...); + * widget->SetRenderWindow(window.Get()); + * + * // If using any of the standard view e.g. vtkContextView, then + * // you can do the following. + * vtkNew view; + * view->SetRenderWindow(window.Get()); + * + * // You can continue to use `window` as a regular vtkRenderWindow + * // including adding renderers, actors etc. + * + * @endcode + * + * @section OpenGLContext OpenGL Context + * + * In QOpenGLWidget (superclass for myQVTKOpenGLNativeWidget), all rendering happens in a + * framebuffer object. Thus, care must be taken in the rendering code to never + * directly re-bind the default framebuffer i.e. ID 0. + * + * myQVTKOpenGLNativeWidget creates an internal QOpenGLFramebufferObject, independent of the + * one created by superclass, for vtkRenderWindow to do the rendering in. This + * explicit double-buffering is useful in avoiding temporary back-buffer only + * renders done in VTK (e.g. when making selections) from destroying the results + * composed on screen. + * + * @section RenderAndPaint Handling Render and Paint. + * + * QWidget subclasses (including `QOpenGLWidget` and `myQVTKOpenGLNativeWidget`) display + * their contents on the screen in `QWidget::paint` in response to a paint event. + * `QOpenGLWidget` subclasses are expected to do OpenGL rendering in + * `QOpenGLWidget::paintGL`. QWidget can receive paint events for various + * reasons including widget getting focus/losing focus, some other widget on + * the UI e.g. QProgressBar in status bar updating, etc. + * + * In VTK applications, any time the vtkRenderWindow needs to be updated to + * render a new result, one call `vtkRenderWindow::Render` on it. + * vtkRenderWindowInteractor set on the render window ensures that as + * interactions happen that affect the rendered result, it calls `Render` on the + * render window. + * + * Since paint in Qt can be called more often then needed, we avoid potentially + * expensive `vtkRenderWindow::Render` calls each time that happens. Instead, + * myQVTKOpenGLNativeWidget relies on the VTK application calling + * `vtkRenderWindow::Render` on the render window when it needs to update the + * rendering. `paintGL` simply passes on the result rendered by the most render + * vtkRenderWindow::Render to Qt windowing system for composing on-screen. + * + * There may still be occasions when we may have to render in `paint` for + * example if the window was resized or Qt had to recreate the OpenGL context. + * In those cases, `myQVTKOpenGLNativeWidget::paintGL` can request a render by calling + * `myQVTKOpenGLNativeWidget::renderVTK`. + * + * @section Caveats + * myQVTKOpenGLNativeWidget only supports **OpenGL2** rendering backend. + * myQVTKOpenGLNativeWidget does not support stereo, + * please use QVTKOpenGLWidget if you need support for stereo rendering + * + * myQVTKOpenGLNativeWidget is targeted for Qt version 5.5 and above. + * + */ +#ifndef myQVTKOpenGLNativeWidget_h +#define myQVTKOpenGLNativeWidget_h + +#include + +#include "QVTKInteractor.h" // needed for QVTKInteractor +#include "vtkGUISupportQtModule.h" // for export macro +#include "vtkNew.h" // needed for vtkNew +#include "vtkSmartPointer.h" // needed for vtkSmartPointer + +class QOpenGLDebugLogger; +class QOpenGLFramebufferObject; +class QVTKInteractor; +class QVTKInteractorAdapter; +class QVTKOpenGLNativeWidgetObserver; +class vtkGenericOpenGLRenderWindow; +class DicomImageView; + +//class VTKGUISUPPORTQT_EXPORT myQVTKOpenGLNativeWidget : public QOpenGLWidget +class myQVTKOpenGLNativeWidget : public QOpenGLWidget +{ + Q_OBJECT + typedef QOpenGLWidget Superclass; +public: + myQVTKOpenGLNativeWidget(QWidget* parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()); + ~myQVTKOpenGLNativeWidget() override; + + //@{ + /** + * Get/Set the currently used vtkGenericOpenGLRenderWindow. + * GetRenderWindow() creates and returns a new vtkGenericOpenGLRenderWindow + * if it is not already provided. + */ + void SetImageView(DicomImageView *v); + DicomImageView* GetImageView(); + + void SetRenderWindow(vtkGenericOpenGLRenderWindow* win); + void SetRenderWindow(vtkRenderWindow* win); + virtual vtkRenderWindow* GetRenderWindow(); + //@} + + /** + * Get the QEvent to VTK events translator. + */ + virtual QVTKInteractorAdapter* GetInteractorAdapter() { return this->InteractorAdapter; } + + /** + * Get the QVTKInteractor that was either created by default or set by the user. + */ + virtual QVTKInteractor* GetInteractor(); + + /** + * Sets up vtkRenderWindow ivars using QSurfaceFormat. + */ + static void copyFromFormat(const QSurfaceFormat& format, vtkRenderWindow* win); + + /** + * Using the vtkRenderWindow, setup QSurfaceFormat. + */ + static void copyToFormat(vtkRenderWindow* win, QSurfaceFormat& format); + + /** + * Returns a typical QSurfaceFormat suitable for most applications using + * myQVTKOpenGLNativeWidget. Note that this is not the QSurfaceFormat that gets used + * if none is specified. That is set using `QSurfaceFormat::setDefaultFormat`. + */ + static QSurfaceFormat defaultFormat(); + + /** + * Enable or disable support for HiDPI displays. + */ + virtual void setEnableHiDPI(bool enable); + virtual bool enableHiDPI() { return this->EnableHiDPI; } + + /** + * Set the cursor on this widget. + */ + void setQVTKCursor(const QCursor &cursor); + +signals: + /** + * This signal will be emitted whenever a mouse event occurs within the QVTK window. + */ + void mouseEvent(QMouseEvent* event); + +protected slots: + /** + * Called as a response to `QOpenGLContext::aboutToBeDestroyed`. This may be + * called anytime during the widget lifecycle. We need to release any OpenGL + * resources allocated in VTK work in this method. + */ + virtual void cleanupContext(); + +private slots: + /** + * recreates the FBO used for VTK rendering. + */ + void recreateFBO(); + + /** + * called before the render window starts to render. We ensure that this->FBO + * is bound and ready to use. + */ + void startEventCallback(); + + /** + * callback for changing the cursor. Called when vtkGenericOpenGLRenderWindow + * fires the CursorChangedEvent. + */ + void cursorChangedCallback(vtkObject* caller, unsigned long vtk_event, + void* client_data, void* call_data); + +protected: + //virtual void wheelEvent(QWheelEvent *event); + + bool event(QEvent* evt) Q_DECL_OVERRIDE; + void initializeGL() Q_DECL_OVERRIDE; + void resizeGL(int w, int h) Q_DECL_OVERRIDE; + void paintGL() Q_DECL_OVERRIDE; + + //void mousePressEvent(QMouseEvent* event) Q_DECL_OVERRIDE; + //void mouseMoveEvent(QMouseEvent* event) Q_DECL_OVERRIDE; + //void mouseReleaseEvent(QMouseEvent* event) Q_DECL_OVERRIDE; + //void mouseDoubleClickEvent(QMouseEvent* event) Q_DECL_OVERRIDE; + + /** + * This method is called to indicate that vtkRenderWindow needs to reinitialize + * itself before the next render (done in myQVTKOpenGLNativeWidget::paintGL). + * This is needed when the context gets recreated + * or the default FrameBufferObject gets recreated, for example. + */ + void requireRenderWindowInitialization(); + + /** + * This method may be called in `paintGL` to request VTK to do a render i.e. + * trigger render on the render window via its interactor. + * + * It will return true if render (or an equivalent action) was performed to + * update the frame buffer made available to VTK for rendering with latest + * rendering. + * + * Default implementation never returns false. However, subclasses can return + * false to indicate to myQVTKOpenGLNativeWidget that it cannot generate a reasonable + * image to be displayed in myQVTKOpenGLNativeWidget. In which case, the `paintGL` + * call will return leaving the `defaultFramebufferObject` untouched. + * + * Since by default `QOpenGLWidget::UpdateBehavior` is set to + * QOpenGLWidget::PartialUpdate, this means whatever was rendered in the frame + * buffer in most recent successful call will be preserved, unless the widget + * was forced to recreate the FBO as a result of resize or screen change. + * + * @sa Section @ref RenderAndPaint. + */ + virtual bool renderVTK(); + +protected: + vtkSmartPointer RenderWindow; + QVTKInteractorAdapter* InteractorAdapter; + + bool EnableHiDPI; + int OriginalDPI; + + static const double DevicePixelRatioTolerance; + +private: + Q_DISABLE_COPY(myQVTKOpenGLNativeWidget); + + /** + * Called when vtkCommand::WindowFrameEvent is fired by the + * vtkGenericOpenGLRenderWindow. + */ + void windowFrameEventCallback(); + + QOpenGLFramebufferObject* FBO; + bool InPaintGL; + bool DoVTKRenderInPaintGL; + vtkNew Observer; + friend class QVTKOpenGLNativeWidgetObserver; + QOpenGLDebugLogger* Logger; + DicomImageView *view; +}; + +#endif diff --git a/src/include/view/subview/DicomTreeModel.h b/src/include/view/subview/DicomTreeModel.h new file mode 100644 index 0000000..9a8a066 --- /dev/null +++ b/src/include/view/subview/DicomTreeModel.h @@ -0,0 +1,41 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef DICOMTREEMODEL_H +#define DICOMTREEMODEL_H + +#include +#include +#include +class DcmFileFormat; + +/*! Model for a tree view to represent DICOM dataset structire. + */ + + +class DicomTreeModel : public QStandardItemModel +{ + Q_OBJECT + +public: + + DicomTreeModel(DcmFileFormat *file, QObject *parent = 0); + + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + +private: + //Attach list of tags to tree model item. + template + static void attachTagList(QStandardItem *item, T *dataset); + +}; + +#endif // DICOMTREEMODEL_H diff --git a/src/include/view/subview/customwindow.h b/src/include/view/subview/customwindow.h new file mode 100644 index 0000000..bab744d --- /dev/null +++ b/src/include/view/subview/customwindow.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include "ui_customwindow.h" + +class DicomImageView; +class Customwindow : public QDialog +{ + Q_OBJECT + +public: + Customwindow(QWidget *parent = Q_NULLPTR); + ~Customwindow(); + + void setCurrentView(DicomImageView *cur); + +public slots: + void onBtnOKClicked(); + void onBtnCancelClicked(); + +private: + Ui::Customwindow ui; + DicomImageView *m_curV; +}; diff --git a/src/include/view/subview/gridpopwidget.h b/src/include/view/subview/gridpopwidget.h new file mode 100644 index 0000000..7c86876 --- /dev/null +++ b/src/include/view/subview/gridpopwidget.h @@ -0,0 +1,31 @@ +#ifndef GRIDPOPWIDGET_H +#define GRIDPOPWIDGET_H + +#include + +namespace Ui { + class GridPopWidget; +} + +class GridPopWidget : public QWidget { + Q_OBJECT + + public: + explicit GridPopWidget(QWidget *parent = nullptr); + ~GridPopWidget(); + + signals: + void Signal_ViewLayout(int col, int row); + + protected: + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void hideEvent(QHideEvent *e); + + private: + Ui::GridPopWidget *ui; + + QList > wArr; +}; + +#endif // GRIDPOPWIDGET_H diff --git a/src/include/view/subview/importtitlebar.h b/src/include/view/subview/importtitlebar.h new file mode 100644 index 0000000..434d738 --- /dev/null +++ b/src/include/view/subview/importtitlebar.h @@ -0,0 +1,46 @@ +#ifndef _IMPORTTITLEBAR_H_ +#define _IMPORTTITLEBAR_H_ + +#include + +class QButtonGroup; +class QHBoxLayout; +class QLabel; +class QSpacerItem; +class QPushButton; + +class ImportTitleBar : public QWidget +{ + Q_OBJECT + +public: + explicit ImportTitleBar(QWidget *parent = Q_NULLPTR); + ~ImportTitleBar(); + + void setTitleText(const QString& title); + +public slots: + void onButtonClicked(int index); + +signals: + void sigClose(); + void sigShowMaximum(); + void sigShowMinimum(); + +private: + void initUi(); + void initSys(); + +private: + QHBoxLayout *m_pMainLayout; + QLabel *m_pLogoLabel; + QLabel *m_pTitleLabel; + QSpacerItem *m_pSpacerItem; + QPushButton *m_pCloseButton; + QPushButton *m_pMinimumButton; + QPushButton *m_pMaximumButton; + QButtonGroup *m_pButtonGroup; +}; + +#endif // !_IMPORTTITLEBAR_H_ + diff --git a/src/include/view/subview/importwidget.h b/src/include/view/subview/importwidget.h new file mode 100644 index 0000000..762cd37 --- /dev/null +++ b/src/include/view/subview/importwidget.h @@ -0,0 +1,142 @@ +#ifndef _IMPORTWIDGET_H_ +#define _IMPORTWIDGET_H_ + +#include +#include +#include +#include +#include +#include "base/dicomviewerhelper.h" +#include "base/dicomviewertype.h" +#include "queryworker.h" +#include "moveworker.h" +#include "radiusprogressbar.h" +//#include "radiusprogressbar.h" + +class QVBoxLayout; +class QGridLayout; +class QHBoxLayout; +class QPushButton; +class QComboBox; +class QDateEdit; +class QLineEdit; +class QTableView; +class QStandardItemModel; +class QItemSelectionModel; +class ConfigurationDialog; +class PromptDialog; +class CallbackHelper; +class DcmDataset; +class QProgressBar; +class QSpacerItem; + +class ImportWidget : public QDialog +{ + Q_OBJECT + +public: + explicit ImportWidget(QWidget *parent = Q_NULLPTR); + ~ImportWidget(); + +signals: + void sigMoveDone(int code, std::string dir); + +public slots: + void close(); + void showMaximum(); + void showMinumum(); + void query(); + void clear(); + void configure(); + void onPacsInfoUpdated(); + void onDateFilterChanged(const QString &); + + void onTitleBarDestroyed(); + + void onQueryDone(int); + void onStudyFoundResult(PACSStudyInfo); + void onSeriesFoundResult(PACSSeriesInfo); + //////void onFoundResult(int index, DcmDataset *response); + void onStudySelected(const QModelIndex &); + void onSeriesSelected(const QModelIndex &); + void onSeriesDoubleClicked(const QModelIndex &); + void moveDone(int, QString); + void moveProgress(int, int); + void moveStoreProgress(int, std::string); + +protected: + virtual bool eventFilter(QObject *obj, QEvent *event); + +private: + void initUi(); + void initSys(); + void initFilterPacs(); + void initFilterModality(); + void initFilterDate(); + void initFilterDicom(); + void initStudyHeader(); + void initStudy(); + void initSeriesHeader(); + void initSeries(); + void getNetParams(std::string& peerIP, unsigned long& peerPort, std::string& peerTitle, unsigned long& ourPort, std::string& ourTitle); + void updateStudyView(); + void updateSeriesView(); + void getDateIntervalForQuery(QString& startDate, QString& endDate); + +private: + QVBoxLayout *m_pMainLayout; + QWidget *m_pTitleBar; + QGridLayout *m_pFilterLayout; + QPushButton *m_pSettingButton; + QComboBox *m_pPacsComboBox; + QComboBox *m_pModalityComboBox; + QDateEdit *m_pStartDate; + QDateEdit *m_pEndDate; + QComboBox *m_pDicomComboBox; + QComboBox *m_pDateComboBox; + QLineEdit *m_pDicomEdit; + QPushButton *m_pSearchButton; + QPushButton *m_pClearButton; + QTableView *m_pStudyResult; + QTableView *m_pSeriesResult; + + QWidget *m_pFilterWidget; + QWidget *m_pProgressWidget; + QHBoxLayout *m_pProgressLayout; + QSpacerItem *m_pProgressItem; + QProgressBar *m_pProgressBar; + //RadiusProgressBar *m_pProgressBar; + + ConfigurationDialog *m_pSettingDialog; + PromptDialog* m_pMsgBox; + QList m_lHosts; + + QStandardItemModel *m_pStudyModel; + //QItemSelectionModel *m_pStudySelectionModel; + QStringList m_lStudyHeaders; + + QStandardItemModel *m_pSeriesModel; + //QItemSelectionModel *m_pSeriesSelectionModel; + QStringList m_lSeriesHeaders; + + CallbackHelper *m_CallbackHelper; + QList m_studyInfo; + QList m_seriesInfo; + + int currentSeriesRow; + + QThread* m_pQueryWorkerThread; + QueryWorker* m_pQueryWorker; + QThread* m_pMoveWorkerThread; + MoveWorker* m_pMoveWorker; + +private: + struct + { + bool dragging; + QPoint dragStartPosition; + } m_dragState; + +}; + +#endif // !_IMPORTWIDGET_H_ diff --git a/src/include/view/subview/metaDataWindow.h b/src/include/view/subview/metaDataWindow.h new file mode 100644 index 0000000..3635ed5 --- /dev/null +++ b/src/include/view/subview/metaDataWindow.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include +#include +#include +#include "DicomTreeModel.h" + +class DcmFileFormat; +class DcmDataset; +class metaDataWindow : public QMdiSubWindow +{ + Q_OBJECT + +public: + metaDataWindow(DcmFileFormat *file, QWidget *parent = 0); + ~metaDataWindow(); + + /** + * Tells whether this DICOM window displays an image. + * @return true if there is an image displayed. + */ + bool containsImage() const; +private: + Q_DISABLE_COPY(metaDataWindow) + + DcmFileFormat *m_file = nullptr; + //DcmDataset *m_dataset = nullptr; + DicomTreeModel *m_treeModel; + QTreeView *m_treeView; + bool m_valid; +}; + diff --git a/src/include/view/subview/moveworker.h b/src/include/view/subview/moveworker.h new file mode 100644 index 0000000..f5908b4 --- /dev/null +++ b/src/include/view/subview/moveworker.h @@ -0,0 +1,40 @@ +#ifndef _MOVEWORKER_H_ +#define _MOVEWORKER_H_ + +#include +#include "../../../include/base/dicomviewertype.h" +#include "../../../include/callback/callbackhelper.h" +#include "../../../include/callback/cmovecallback.h" +#include "../../../include/callback/cmovestorescpcallback.h" + + +class MoveWorker : public QObject +{ + Q_OBJECT + +public: + explicit MoveWorker(const QString& peerIP, unsigned long long peerPort, const QString& peerTitle, unsigned long long ourPort, const QString& ourTitle, const QString& outputDirectory, QObject *parent = Q_NULLPTR); + void setOutputDirectory(const QString& outputDir); + Q_INVOKABLE void moveBySeriesUID(const QString& studyInstanceUID, const QString& seriesInstanceUID); + +signals: + void notifyMoveDone(int, QString dir); + void notifyMoveProgress(int, int); + void notifyMoveStoreProgress(int, std::string); + +public slots: + void onMoveProgress(int, int); + void onMoveStoreProgress(int, std::string); + +private: + QString m_strPeerIP_; + unsigned long long m_ulPeerPort_; + QString m_strPeerTitle_; + unsigned long long m_ulOurPort_; + QString m_strOurTitle_; + QString m_strOutputDirectory_; + CallbackHelper* m_pCallbackHelper; +}; + + +#endif // !_MOVEWORKER_H_ diff --git a/src/include/view/subview/mytitlebar.h b/src/include/view/subview/mytitlebar.h new file mode 100644 index 0000000..0665e2f --- /dev/null +++ b/src/include/view/subview/mytitlebar.h @@ -0,0 +1,106 @@ +#include +#include +#include +#include + +//enum ButtonType +//{ +// MIN_BUTTON = 0, // Minimize and close buttons; +// MIN_MAX_BUTTON , // Minimize, maximize and close buttons; +// ONLY_CLOSE_BUTTON // Only close button; +//}; + +class MyTitleBar : public QFrame +{ + Q_OBJECT + +public: + MyTitleBar(QWidget *parent); + //Here parent does not give the default value of NULL, to ensure that the parent pointer must be assigned when the MyTitleBar object is created; and the assignment is not NULL; + ~MyTitleBar(); + void SetHighlight(bool yes); + // Set the background color of the title bar and whether to set the background color of the title bar to be transparent; +//protected: +// void paintEvent(QPaintEvent *event); + + // Set the button type on the title bar; + //void setButtonType(ButtonType buttonType); + // Set whether the title in the title bar will scroll; you can see the effect for details; + //void setTitleRoll(); + // Set the width of the window border; + //void setWindowBorderWidth(int borderWidth); + + // Save/Get the position and size of the window before maximizing; + //void saveRestoreInfo(const QPoint point, const QSize size); + //void getRestoreInfo(QPoint& point, QSize& size); + +private: + // Set the title bar icon; + void setTitleIcon(QString filePath, QSize IconSize = QSize(25, 25)); + // Set the title content; + void setTitleContent(QString titleContent, int titleFontSize = 9); + // Set the length of the title bar; + void setTitleWidth(int width); + void setBackgroundColor(QColor pickColor); + + + void mouseDoubleClickEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *event); + // void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + + // Initialize the control; + void initControl(); + // Binding of signal slots; + void initConnections(); + // Load the style file; + void loadStyleSheet(const QString &sheetName); + +signals: + // The signal triggered by the button; + //void signalViewClicked(); + void signalButtonMaxClicked(); + void signalButtonCloseClicked(); + +private slots: + // The slot triggered by the button; + // void onButtonMinClicked(); + //void onButtonRestoreClicked(); + void onButtonMaxClicked(); + void onButtonCloseClicked(); + //void onRollTitle(); + +private: + QLabel* m_pIcon; // title bar icon; + QLabel* m_pTitleContent; // Title bar content; + //QPushButton* m_pButtonMin; // Minimize button; + //QPushButton* m_pButtonRestore; // Maximize restore button; + QPushButton* m_pButtonMax; // Maximize button; + QPushButton* m_pButtonClose; // Close button; + + //QColor m_color; + // The background color of the title bar; + // int m_colorR; + // int m_colorG; + // int m_colorB; + //int m_alpha; + // maximize and minimize variables; + // QPoint m_restorePos; + //QSize m_restoreSize; + // Variables of moving window; + bool m_isPressed; + // QPoint m_startMovePos; + // marquee effect clock in title bar; + // QTimer m_titleRollTimer; + // Title bar content; + QString m_titleContent; + // Button type; + //ButtonType m_buttonType; + // width of window border; + //int m_windowBorderWidth; + // Whether the title bar is transparent; + //bool m_isTransparent; + + QIcon icon_close; + QIcon icon_max; +}; diff --git a/src/include/view/subview/pacsconfiguration.h b/src/include/view/subview/pacsconfiguration.h new file mode 100644 index 0000000..469d079 --- /dev/null +++ b/src/include/view/subview/pacsconfiguration.h @@ -0,0 +1,110 @@ +#ifndef _PACS_CONFIGURATION_H_ +#define _PACS_CONFIGURATION_H_ + +#include +#include +#include "base/dicomviewerhelper.h" + +class QVBoxLayout; +class QHBoxLayout; +class QGridLayout; +class QLabel; +class QLineEdit; +class QPushButton; +class QTableView; +class QFrame; +class QSpacerItem; +class QStandardItemModel; +class QItemSelectionModel; +class PromptDialog; + +class ConfigurationDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ConfigurationDialog(QWidget *parent = Q_NULLPTR); + ~ConfigurationDialog(); + +public slots: + void close(); + void save(); + void del(); + void cancel(); + void add(); + void modify(); + void onSelected(const QModelIndex &); + + void onTitleBarDestroyed(); + +protected: + virtual bool eventFilter(QObject *obj, QEvent *event); + +// tell upper widget to update pacs info if added or modifed or deleted +signals: + void updatePacsInfo(); + +private: + void initUi(); + void initSys(); + void initHeaderInfo(); + void initPacsInfo(); + void updatePacsView(); + void clearModify(); + +private: + QVBoxLayout *m_pMainLayout; + QWidget *m_pTitleBar; + QWidget *m_pOurInfoWidget; + QHBoxLayout *m_pOurInfoLayout; + QLabel *m_pOurPortLabel; + QLineEdit *m_pOurPortEdit; + QLabel *m_pOurTitleLabel; + QLineEdit *m_pOurTitleEdit; + QPushButton *m_AdvancedSettingsButton; + QFrame *m_pOurInfoEndLine; + QWidget *m_pToolsWidget; + QHBoxLayout *m_pToolsLayout; + QLabel *m_pTitleLabel; + QSpacerItem *m_pSpacerItem; + QPushButton *m_pDelButton; + QTableView *m_pPacsInfo; + QWidget *m_pEditWidget; + QGridLayout *m_pEditLayout; + QLabel *m_pPeerIpAddressLabel; + QLabel *m_pPeerPortLabel; + QLabel *m_pPeerTitleLabel; + QLabel *m_pPeerDescritopnLabel; + QLineEdit *m_pPeerIpAddressEdit; + QLineEdit *m_pPeerPortEdit; + QLineEdit *m_pPeerTitleEdit; + QLineEdit *m_pPeerDescriptionEdit; + QPushButton *m_pAddButton; + QPushButton *m_pModifyButton; + QFrame *m_pEditEndLine; + QWidget *m_pActionWidget; + QHBoxLayout *m_pActionLayout; + QSpacerItem *m_pSpacerItem1; + QPushButton *m_pSaveButton; + QPushButton *m_pCancelButton; + + QStandardItemModel *m_pPacsModel; + //QItemSelectionModel *m_pPacsSelectionModel; + QStringList m_lPacsInfoHeaders; + int currentRow; + PromptDialog *m_pMsgDialog; + + QList m_lHosts; + QList m_lHostsNew; // 如果中间做了操作,但是最后Cancel掉,就用这个记录中间的操作吧,lol + +private: + struct + { + bool dragging; + QPoint dragStartPosition; + } m_dragState; + +}; + +#endif // !_PACS_CONFIGURATION_H_ + diff --git a/src/include/view/subview/pacsconfiguretitlebar.h b/src/include/view/subview/pacsconfiguretitlebar.h new file mode 100644 index 0000000..387ea60 --- /dev/null +++ b/src/include/view/subview/pacsconfiguretitlebar.h @@ -0,0 +1,39 @@ +#ifndef _PACS_CONFIGURE_TITLEBAR_H_ +#define _PACS_CONFIGURE_TITLEBAR_H_ + +#include + +class QHBoxLayout; +class QLabel; +class QPushButton; +class QSpacerItem; + +class ConfigurationTitleBar : public QWidget +{ + Q_OBJECT + +public: + explicit ConfigurationTitleBar(QWidget *parent = Q_NULLPTR); + ~ConfigurationTitleBar(); + + void setTitleText(const QString& title); + +signals: + void sigClose(); + +private: + void initUi(); + void initSys(); + +private: + QHBoxLayout *m_pMainLayout; + QLabel *m_pLogoLabel; + QLabel *m_pTitleLabel; + QSpacerItem *m_pSpacerItem; + QPushButton *m_pCloseButton; +}; + + + +#endif // !_PACS_CONFIGURE_TITLEBAR_H_ + diff --git a/src/include/view/subview/queryworker.h b/src/include/view/subview/queryworker.h new file mode 100644 index 0000000..f1966e3 --- /dev/null +++ b/src/include/view/subview/queryworker.h @@ -0,0 +1,45 @@ +#ifndef _QUERYWORKER_H_ +#define _QUERYWORKER_H_ + +#include +#include "../../../include/callback/callbackhelper.h" +#include "../../../include/callback/cfindcallback.h" +#include "../../../include/base/dicomviewertype.h" + +class DcmDataset; + +class QueryWorker : public QObject +{ + Q_OBJECT + +public: + explicit QueryWorker(const QString& peerIP, unsigned long peerPort, const QString& peerTitle, const QString& ourTitle, QObject *parent = Q_NULLPTR); + void setPacsInfo(const QString& peerIP, unsigned long peerPort, const QString& peerTitle, const QString& ourTitle); + Q_INVOKABLE void queryByPatientName(const QString& patientName); + Q_INVOKABLE void queryByPatientNameAndDate(const QString& patientName, const QString& studyStartDate, const QString& studyEndDate); + Q_INVOKABLE void queryByPatientID(const QString& patientID); + Q_INVOKABLE void queryByPatientIDAndDate(const QString& patientID, const QString& studyStartDate, const QString& studyEndDate); + Q_INVOKABLE void queryByAccessNo(const QString& accsessionNo); + Q_INVOKABLE void queryByAccessNoAndDate(const QString& accessionNo, const QString& studyStartDate, const QString& studyEndDate); + Q_INVOKABLE void queryBySeriesUID(const QString& studyInstanceUID); + +signals: + void sendFindDone(int); + void sendStudyItemFound(PACSStudyInfo); + void sendSeriesItemFound(PACSSeriesInfo); + +public slots: + void onFoundResult(int index, DcmDataset *response); + +private: + QString m_strPeerIP_; + unsigned long m_ulPeerPort_; + QString m_strPeerTitle_; + QString m_strOurTitle_; + CallbackHelper* m_pCallbackHelper; + int m_iQueryLevel; // 0 - study, 1 - series // Here, should be a better solution + +}; + +#endif // !_QUERYWORKER_H_ + diff --git a/src/include/view/subview/radiusprogressbar.h b/src/include/view/subview/radiusprogressbar.h new file mode 100644 index 0000000..81a21bd --- /dev/null +++ b/src/include/view/subview/radiusprogressbar.h @@ -0,0 +1,20 @@ +#ifndef _RADIUS_PROGRESSBAR_H_ +#define _RADIUS_PROGRESSBAR_H_ + +#include +#include + +class RadiusProgressBar : public QProgressBar +{ + Q_OBJECT + +public: + explicit RadiusProgressBar(QWidget *parent = Q_NULLPTR); + ~RadiusProgressBar(); + +protected: + virtual void paintEvent(QPaintEvent *); +}; + +#endif // !_RADIUS_PROGRESSBAR_H_ + diff --git a/src/include/view/thumbnailImage.h b/src/include/view/thumbnailImage.h new file mode 100644 index 0000000..dca0d1a --- /dev/null +++ b/src/include/view/thumbnailImage.h @@ -0,0 +1,51 @@ +#pragma once +//#include +//#include +//#include +//#include +//#include +#include "global/QGlobals.h" +#include +#include +#include +#include +#include +#include +class thumbnailImage : public QFrame +{ + Q_OBJECT + +public: + thumbnailImage(QWidget *parent, SeriesInfo_t* series_info); + ~thumbnailImage(); + void setHighlight(bool yes); + SeriesInfo_t* getSeriesInfo() + { + return m_series_info; + } +//protected: +// void paintEvent(QPaintEvent *event)override; +signals: + void Signal_ThumbClicked(thumbnailImage*); + +protected: + void mouseReleaseEvent(QMouseEvent* event); + void mousePressEvent(QMouseEvent* event); + void mouseMoveEvent(QMouseEvent *e); + + +private: + + //void setBackgroundColor(QColor pickColor); + + + + QLabel *m_descri; + QLabel *m_slicenum; + QLabel *m_pixmap; + SeriesInfo_t* m_series_info; + QPoint drag_org_; + + //bool m_isTransparent; + //QColor m_color; +}; diff --git a/src/include/view/thumbnailbarwidget.h b/src/include/view/thumbnailbarwidget.h new file mode 100644 index 0000000..6b9d939 --- /dev/null +++ b/src/include/view/thumbnailbarwidget.h @@ -0,0 +1,51 @@ +锘#ifndef THUMBNAILBARWIDGET_H +#define THUMBNAILBARWIDGET_H + +#include +#include +#include +#include +#include +#include "global/QGlobals.h" +#include "QScrollArea.h" + +class ImageInstance; +class SeriesInstance; +class thumbnailImage; +class DicomImageView; + +class ThumbnailBarWidget : public QFrame { + Q_OBJECT + + public: + explicit ThumbnailBarWidget(QWidget *parent = nullptr); + ~ThumbnailBarWidget(); + + thumbnailImage *getCurrentImageLabel() const { + return currentImageLabel; + } + void updateThumbnailBar(); +//protected: +// void paintEvent(QPaintEvent *)override; + + Q_SIGNALS: + void Signal_CopyDicomView(SeriesInfo_t* serie_info); + void Signal_ThumbClicked(thumbnailImage* thumb); + + public Q_SLOTS: + void Slot_setCurrentThumbnail(DicomImageView *view); + void SLot_ThumbClicked(thumbnailImage* thumb); + void clear(); + + private: + void setCurrentImageLabel(thumbnailImage *imageLabel); + thumbnailImage* createThumbnailImage(QWidget *parent, SeriesInfo_t* series_info); + + QWidgetList LabelList; + QList imageLabelList; + thumbnailImage *currentImageLabel; + QWidget* seriesPanel = nullptr; + QScrollArea * scrollArea = nullptr; +}; + +#endif // THUMBNAILBARWIDGET_H diff --git a/src/include/view/viewcontainerwidget.h b/src/include/view/viewcontainerwidget.h new file mode 100644 index 0000000..9fb91d2 --- /dev/null +++ b/src/include/view/viewcontainerwidget.h @@ -0,0 +1,64 @@ +锘#pragma once +#include +#include +#include "global/QGlobals.h" +//#include "modalityproperty.h" +#include "dicomimageview.h" + +class QGridLayout; +class DicomImageView; +class SeriesInstance; + +class ViewContainerWidget : public QFrame { + Q_OBJECT + +public: + + explicit ViewContainerWidget(QWidget *parent = nullptr); + ~ViewContainerWidget(); + + + SeriesInstance* getCurrentSeries() const; + DicomImageView* getCurrentView() const; + DicomImageView* getNextView() const; + QList getViewList() const; + void getRelevantViewList(const UniqueIDInfo& unique, QList & viewList); + + + void setCurrentView(DicomImageView *view); + void emptyCurrentView(); + + void replaceViewWithSerie(UniqueIDInfo_t* unique_info, DicomTagInfo_t* tag_info, + DicomImageView* curV, SeriesInstance* old = nullptr, bool copy = false); + + //fusion + void toggleViewWithFusion(); + void removeCurrentViewWithFusion(); + void SetInteractionMode(int InteractionMode); + +signals: + void Signal_NotifyThumbnail(DicomImageView *view); + + +public slots: + void Slot_ViewEmpty(DicomImageView *view); + void Slot_SetViewLayout(int col, int row); + void Slot_ViewClicked(DicomImageView *view); + void Slot_ViewDoubleClicked(DicomImageView *view); + void Slot_DragDropEvent(DicomImageView *view, thumbnailImage* tb); + void Slot_ThumbnailClickEvent(thumbnailImage* tb); + void Slot_SyncEvent(DicomImageView *view, int interactionMode, void* calldata); + +protected: + void resizeEvent(QResizeEvent *e); + +private: + //fusion + void replaceViewWithFusion(); + bool checkFusionStatus(DicomImageView *base, DicomImageView* overlap); + + DicomImageView *createImageView(QWidget* parent); + QList view_list_; + DicomImageView *current_view_ = nullptr; + bool maxed_ = false; +}; diff --git a/src/src/QDicomViewer.cpp b/src/src/QDicomViewer.cpp new file mode 100644 index 0000000..32494a2 --- /dev/null +++ b/src/src/QDicomViewer.cpp @@ -0,0 +1,1088 @@ +锘#include "QDicomViewer.h" +#include "global/include_all.h" +#include "base/seriesinstance.h" +#include "view/subview/gridpopwidget.h" +#include "view/ViewContainerWidget.h" +#include "base/DicomLoader.h" + +#include +#include +#include +#include +#include +#include +#include "ControlPointActor.h" +#include "RulerAnnotationActor.h" +#include "AngleAnnotationActor.h" +#include "TextAnnotationActor.h" +#include "EllipseAnnotationActor.h" +#include "OpenPolyAnnotationActor.h" +#include "ArrowAnnotationActor.h" +#include "pqFontPropertyWidget.h" +#include "pqVCRToolbar.h" + +#include + +/************************************************************************ +* Consider: +* 1. [Done]series id only maps to a single instance, this situation needs to be improved. +* 2. [Done]Corner Info Auto tune +* 3. [Undone]disable open icon when dicom data loading is not finished +* 4. [Done]Import from PACS inherits Qthread in the near future! + +* Notice: +* 1. You'd better set to new instance before delete the older, may leads to crash problem. +* 2. + +* Bug: +* 1. getRelevantViewList() should consider in memory not in view instance +/************************************************************************/ +#define SINGLE_INSTANCE 1 + +#define addbutton(name,png)\ + QToolButton* btn##name = new QToolButton(this);\ + {\ + QPixmap map(png);\ + btn##name->setIcon(QIcon(map));\ + btn##name->setMinimumSize(QSize(48, 48));\ + btn##name->setMaximumSize(QSize(48, 48));\ + btn##name->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);\ + this->ui->toolBar->addWidget(btn##name);\ + }\ + + +#define addGroupedButton(name,png,group)\ + QToolButton* btn##name = new QToolButton(this);\ + {\ + QPixmap map(png);\ + btn##name->setIcon(QIcon(map));\ + btn##name->setCheckable(true);\ + btn##name->setAutoExclusive(true);\ + btn##name->setMinimumSize(QSize(48, 48));\ + btn##name->setMaximumSize(QSize(48, 48));\ + btn##name->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);\ + group->addButton(btn##name);\ + this->ui->toolBar->addWidget(btn##name);\ + }\ + + +#define addbutton2(name,png)\ + QToolButton* btn##name = new QToolButton(this);\ + {\ + QPixmap map(png);\ + btn##name->setIcon(QIcon(map));\ + btn##name->setMinimumSize(QSize(25, 25));\ + btn##name->setMaximumSize(QSize(25, 25));\ + this->ui->toolBar->addWidget(btn##name); \ + }\ + +QDicomViewer::QDicomViewer(QWidget* parent) :QMainWindow(parent), +ui(new Ui::QDicomViewerClass), +m_import(nullptr) + +{ + ui->setupUi(this); + this->statusBar()->showMessage(tr("Ready")); + QWidget::setWindowTitle(Project_NAME); + + icon_manual.addFile(QStringLiteral(SYNC_MANUAL_URL), QSize(), QIcon::Normal, QIcon::Off); + icon_auto.addFile(QStringLiteral(SYNC_AUTO_URL), QSize(), QIcon::Normal, QIcon::Off); + icon_dis.addFile(QStringLiteral(SYNC_DIS_URL), QSize(), QIcon::Normal, QIcon::Off); + this->Initial(); + + loadStyleSheet("Combinear"); + + QSize size(27, 27); + this->setIconSize(size); + + QScreen* screen = QGuiApplication::primaryScreen(); + QRect rect = screen->geometry(); + FontSizeHelper::desktop_width = rect.width(); + FontSizeHelper::desktop_height = rect.height(); + + displayThumbnailBar(false); + + OrienHelper::init(); +} + +QDicomViewer::~QDicomViewer() { +} + + +void QDicomViewer::loadStyleSheet(const QString& sheetName) +{ + QFile file(":/StyleSheet/" + sheetName + ".qss"); + file.open(QFile::ReadOnly); + if (file.isOpen()) + { + QString styleSheet = this->styleSheet(); + styleSheet += QLatin1String(file.readAll()); + this->setStyleSheet(styleSheet); + } +} + +void QDicomViewer::Initial() +{ + this->createToolButton(); + this->setConnections(); +} + +void QDicomViewer::createToolButton() +{ + //this->ui->vcr_toolbar->setMovable(true); + //this->ui->vcr_toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); + ui->toolBar->setContextMenuPolicy(Qt::ContextMenuPolicy::PreventContextMenu); + ui->toolBar->setFixedHeight(VCRHelper::toolbar_Height); + ui->toolBar->setMovable(false); + ui->toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); + + addbutton(file, ":/InfiniteViewer/Icon/openfile.png"); + addbutton(import, ":/InfiniteViewer/Icon/import.png"); + addbutton(save, ":/InfiniteViewer/Icon/save.png"); + this->ui->toolBar->addSeparator(); + + addbutton(grid, ":/InfiniteViewer/Icon/grid.png"); + addbutton(sync, ":/InfiniteViewer/Icon/sync/sync_dis.png"); + addbutton(anno, ":/InfiniteViewer/Icon/anno.png"); + this->ui->toolBar->addSeparator(); + + QButtonGroup* modegroup = new QButtonGroup(this); + addGroupedButton(slice, ":/InfiniteViewer/Icon/slice.png", modegroup); + addGroupedButton(windowlevel, ":/InfiniteViewer/Icon/windowlevel.png", modegroup); + addGroupedButton(pan, ":/InfiniteViewer/Icon/pan.png", modegroup); + addGroupedButton(zoom, ":/InfiniteViewer/Icon/zoom.png", modegroup); + addGroupedButton(measure, ":/InfiniteViewer/Icon/distance.png", modegroup); + addbutton(empty, ":/InfiniteViewer/Icon/trashbin.png"); + this->ui->toolBar->addSeparator(); + + + addbutton(flip, ":/InfiniteViewer/Icon/flip.png"); + addbutton(cine, ":/InfiniteViewer/Icon/cine.png"); + addbutton(fusion, ":/InfiniteViewer/Icon/fusion.png"); + + QWidget* spacer = new QWidget(this); + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + this->ui->toolBar->addWidget(spacer); + + //this->ui->toolBar_2->setFixedHeight(VCRHelper::toolbtn2_Size); + //this->ui->toolBar_2->setMovable(false); + //this->ui->toolBar_2->setToolButtonStyle(Qt::ToolButtonIconOnly); + + + + act_num_of_minimize = ui->toolBar->actions().count(); + addbutton2(minimize, ":/InfiniteViewer/Icon/minimize.png"); + + act_num_of_maximize = ui->toolBar->actions().count(); + addbutton2(maximize, ":/InfiniteViewer/Icon/maximize-restore.png"); + + act_num_of_close = ui->toolBar->actions().count(); + addbutton2(close, ":/InfiniteViewer/Icon/close.png"); + + act_num_of_fullscreen = ui->toolBar->actions().count(); + addbutton2(fullscreen, ":/InfiniteViewer/Icon/full_screen.png"); + + ui->toolBar->actions().at(act_num_of_minimize)->setVisible(false); + ui->toolBar->actions().at(act_num_of_maximize)->setVisible(false); + ui->toolBar->actions().at(act_num_of_close)->setVisible(false); + + this->SetupFileTool(btnfile); + this->SetupImportTool(btnimport); + this->SetupExportTool(btnsave); + + this->SetupGridTool(btngrid); + this->SetupSyncTool(btnsync); + this->SetupAnnoTool(btnanno); + + this->SetupSliceTool(btnslice); + this->SetupAdjustTool(btnwindowlevel); + this->SetupPanTool(btnpan); + this->SetupZoomTool(btnzoom); + this->SetupMeasureTool(btnmeasure); + this->SetupEmptyTool(btnempty); + + this->SetupFlipTool(btnflip); + this->SetupFusionTool(btnfusion); + this->SetupCineTool(btncine); + + this->SetupFullScreenTool(btnfullscreen); + this->SetupMaximizeTool(btnmaximize); + this->SetupMinimizeTool(btnminimize); + this->SetupCloseTool(btnclose); +} + +void QDicomViewer::SetupFullScreenTool(QToolButton* btnfullscreen) +{ + btnfullscreen->setToolTip(QString("Full screen")); + connect(btnfullscreen, &QToolButton::clicked, this, [=] { + ui->toolBar->actions().at(act_num_of_fullscreen)->setVisible(false); + ui->toolBar->actions().at(act_num_of_minimize)->setVisible(true); + ui->toolBar->actions().at(act_num_of_maximize)->setVisible(true); + ui->toolBar->actions().at(act_num_of_close)->setVisible(true); + + setWindowState(Qt::WindowState::WindowFullScreen); + }); +} + +void QDicomViewer::SetupMaximizeTool(QToolButton* btnmaximize) +{ + btnmaximize->setToolTip(QString("Exit full screen mode")); + connect(btnmaximize, &QToolButton::clicked, this, [=] { + + ui->toolBar->actions().at(act_num_of_minimize)->setVisible(false); + ui->toolBar->actions().at(act_num_of_maximize)->setVisible(false); + ui->toolBar->actions().at(act_num_of_close)->setVisible(false); + ui->toolBar->actions().at(act_num_of_fullscreen)->setVisible(true); + + setWindowState(Qt::WindowState::WindowMaximized); + }); +} + + +void QDicomViewer::SetupMinimizeTool(QToolButton* btnminimize) +{ + btnminimize->setToolTip(QString("Minimize")); + connect(btnminimize, &QToolButton::clicked, this, [=] { + setWindowState(Qt::WindowState::WindowMinimized); + + }); +} + +void QDicomViewer::SetupCloseTool(QToolButton* btnclose) +{ + btnclose->setToolTip(QString("Close")); + connect(btnclose, &QToolButton::clicked, this, [=] { + close(); + }); +} + + + +void QDicomViewer::SetupAnnoTool(QToolButton* annoBtn) +{ + annoBtn->setToolTip(QString("Toggle annotations")); + connect(annoBtn, &QToolButton::clicked, this, [=] { + AnnoHelper::toggleAnno(); + QList viewers = ui->viewContainer->getViewList(); + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + v->updateCornerInfoAll(); + }); + }); + + QMenu* m; + m = new QMenu(this); + connect(m, &QMenu::triggered, this, [=] + { + //load data + m_measure_hidden_action->setChecked(Measure::GetHidden()); + }); + + m_measure_hidden_action = m->addAction(tr("Hide all measurements"), this, [=](bool value) { + //QPixmap map(":/InfiniteViewer/Icon/hidden.png"); + //m_cur_measure = AnnotationActorType::HiddenAnn; + //annoBtn->setIcon(QIcon(map)); + QList viewers = ui->viewContainer->getViewList(); + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + Measure::SetHidden(value); + v->Render(); + + + }); + }); + + m->addSeparator(); + + m_patient_hidden_action = m->addAction(tr("Hide patient data"), this, [=] { + if (m_patient_hidden_action->isChecked()) + { + AnnoHelper::PrivacyOn(); + } + else + { + AnnoHelper::PrivacyOff(); + } + QList viewers = ui->viewContainer->getViewList(); + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + v->updateCornerInfoPrivacy(); + }); + }); + m_patient_hidden_action->setCheckable(true); + m_patient_hidden_action->setChecked(false); + + m->addAction(tr("Show Dicom tags"), this, [&] { + DicomImageView* curV = ui->viewContainer->getCurrentView(); + if (curV->HasSeries()) + { + curV->ShowMetaData(); + } + }); + + m_measure_hidden_action->setCheckable(true); + m_measure_hidden_action->setChecked(false); + + + + annoBtn->setPopupMode(QToolButton::MenuButtonPopup); + annoBtn->setMenu(m); +} + + + + + +void QDicomViewer::executeActiveMeasure(ViewContainerWidget* Container, AnnotationActorType annType) +{ + QList viewers = Container->getViewList(); + DicomImageView* curV = ui->viewContainer->getCurrentView(); + switch (annType) + { + case AnnotationActorType::RulerAnn: + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + RulerAnnotationActor* ann = RulerAnnotationActor::New(); + v->ActiveMeasure(ann); + v->Render(); + }); + break; + case AnnotationActorType::AngleAnn: + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + AngleAnnotationActor* ann = AngleAnnotationActor::New(); + v->ActiveMeasure(ann); + }); + break; + case AnnotationActorType::ClosedPolygonAnn: + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + OpenPolyAnnotationActor* ann = OpenPolyAnnotationActor::New(); + ann->SetClosed(1); + v->ActiveMeasure(ann); + }); + break; + case AnnotationActorType::OpenPolygonAnn: + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + OpenPolyAnnotationActor* ann = OpenPolyAnnotationActor::New(); + v->ActiveMeasure(ann); + }); + break; + case AnnotationActorType::ArrowAnn: + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + ArrowAnnotationActor* ann = ArrowAnnotationActor::New(); + v->ActiveMeasure(ann); + }); + break; + case AnnotationActorType::EllipseAnn: + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + EllipseAnnotationActor* ann = EllipseAnnotationActor::New(); + v->ActiveMeasure(ann); + }); + break; + case AnnotationActorType::TextAnn: + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + TextAnnotationActor* ann = TextAnnotationActor::New(); + v->ActiveMeasure(ann); + }); + break; + case AnnotationActorType::DeleteSelectedAnn: + if (curV != nullptr) + { + curV->DeleteSelectedMeasure(); + } + break; + case AnnotationActorType::DeleteSliceAnn: + if (curV != nullptr) + { + curV->DeleteCurrentSliceMeasure(); + } + break; + case AnnotationActorType::DeleteSeriesAnn: + if (curV != nullptr) + { + curV->DeleteCurrentSeriesMeasure(); + } + break; + default: + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + RulerAnnotationActor* ann = RulerAnnotationActor::New(); + v->ActiveMeasure(ann); + }); + break; + } + + +} +void QDicomViewer::SetupMeasureTool(QToolButton* measureBtn) +{ + measureBtn->setToolTip(QString("Measurements")); + + connect(measureBtn, &QToolButton::clicked, this, [=] { + executeActiveMeasure(ui->viewContainer, m_cur_measure); + }); + + QMenu* m; + m = new QMenu(this); + + m->addAction(tr("Length"), this, [=] { + measureBtn->setChecked(true); + QPixmap map(":/InfiniteViewer/Icon/distance.png"); + measureBtn->setIcon(QIcon(map)); + m_cur_measure = AnnotationActorType::RulerAnn; + + QList viewers = ui->viewContainer->getViewList(); + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + RulerAnnotationActor* ann = RulerAnnotationActor::New(); + v->ActiveMeasure(ann); + }); + }); + + m->addAction(tr("Angle"), this, [=] { + measureBtn->setChecked(true); + QPixmap map(":/InfiniteViewer/Icon/angle.png"); + measureBtn->setIcon(QIcon(map)); + m_cur_measure = AnnotationActorType::AngleAnn; + + QList viewers = ui->viewContainer->getViewList(); + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + AngleAnnotationActor* ann = AngleAnnotationActor::New(); + v->ActiveMeasure(ann); + }); + }); + + m->addAction(tr("Closed polygon"), this, [=] { + measureBtn->setChecked(true); + QPixmap map(":/InfiniteViewer/Icon/polygon.png"); + measureBtn->setIcon(QIcon(map)); + m_cur_measure = AnnotationActorType::ClosedPolygonAnn; + + QList viewers = ui->viewContainer->getViewList(); + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + OpenPolyAnnotationActor* ann = OpenPolyAnnotationActor::New(); + ann->SetClosed(1); + v->ActiveMeasure(ann); + }); + }); + + m->addAction(tr("Open polygon"), this, [=] { + measureBtn->setChecked(true); + QPixmap map(":/InfiniteViewer/Icon/polyline.png"); + measureBtn->setIcon(QIcon(map)); + m_cur_measure = AnnotationActorType::OpenPolygonAnn; + + QList viewers = ui->viewContainer->getViewList(); + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + OpenPolyAnnotationActor* ann = OpenPolyAnnotationActor::New(); + v->ActiveMeasure(ann); + }); + }); + + + m->addAction(tr("Arrow"), this, [=] { + measureBtn->setChecked(true); + QPixmap map(":/InfiniteViewer/Icon/arrow.png"); + measureBtn->setIcon(QIcon(map)); + m_cur_measure = AnnotationActorType::ArrowAnn; + + QList viewers = ui->viewContainer->getViewList(); + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + ArrowAnnotationActor* ann = ArrowAnnotationActor::New(); + v->ActiveMeasure(ann); + }); + }); + + + m->addAction(tr("Ellipse"), this, [=] { + QPixmap map(":/InfiniteViewer/Icon/ellipse.png"); + measureBtn->setIcon(QIcon(map)); + m_cur_measure = AnnotationActorType::EllipseAnn; + + QList viewers = ui->viewContainer->getViewList(); + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + EllipseAnnotationActor* ann = EllipseAnnotationActor::New(); + v->ActiveMeasure(ann); + }); + }); + m->addAction(tr("Text"), this, [=] { + measureBtn->setChecked(true); + QPixmap map(":/InfiniteViewer/Icon/text.png"); + measureBtn->setIcon(QIcon(map)); + m_cur_measure = AnnotationActorType::TextAnn; + + QList viewers = ui->viewContainer->getViewList(); + std::for_each(viewers.begin(), viewers.end(), [=](DicomImageView* v) { + TextAnnotationActor* ann = TextAnnotationActor::New(); + v->ActiveMeasure(ann); + }); + }); + + m->addSeparator(); + + + m->addAction(tr("Delete selected"), this, [=] { + measureBtn->setChecked(true); + m_cur_measure = AnnotationActorType::DeleteSelectedAnn; + DicomImageView* curV = ui->viewContainer->getCurrentView(); + if (curV != nullptr) + { + curV->DeleteSelectedMeasure(); + } + }); + m->addAction(tr("Delete all in current slice"), this, [=] { + measureBtn->setChecked(true); + m_cur_measure = AnnotationActorType::DeleteSliceAnn; + DicomImageView* curV = ui->viewContainer->getCurrentView(); + if (curV != nullptr) + { + curV->DeleteCurrentSliceMeasure(); + } + }); + m->addAction(tr("Delete all in current series"), this, [=] { + measureBtn->setChecked(true); + m_cur_measure = AnnotationActorType::DeleteSeriesAnn; + + DicomImageView* curV = ui->viewContainer->getCurrentView(); + if (curV != nullptr) + { + curV->DeleteCurrentSeriesMeasure(); + } + }); + + + measureBtn->setPopupMode(QToolButton::MenuButtonPopup); + measureBtn->setMenu(m); +} + +void QDicomViewer::createVCRToolbar(DicomImageView* v) +{ + pqVCRToolbar* vcr_toolbar = new pqVCRToolbar(this); + //this->addToolBar(Qt::ToolBarArea::AllToolBarAreas, vcr_toolbar); + vcr_toolbar->setAllowedAreas(Qt::AllToolBarAreas); + vcr_toolbar->setMovable(false); + vcr_toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); + vcr_toolbar->setFixedWidth(VCRHelper::vcr_toolbar_Width); + //vcr_toolbar->move(0, 0); + v->setVCRToolbar(vcr_toolbar); +} + +void QDicomViewer::SetupCineTool(QToolButton* cineBtn) +{ + cineBtn->setToolTip(QString("Cine")); + connect(cineBtn, &QToolButton::clicked, this, [&] { + DicomImageView* curV = ui->viewContainer->getCurrentView(); + if (curV->HasSeries()) + { + if (curV->IsCine()) + { + curV->setVCRVisible(!curV->isVCRVisible()); + } + else + { + createVCRToolbar(curV); + curV->cineModeOn(); + } + + + } + }); + + + +} +void QDicomViewer::SetupFusionTool(QToolButton* fusionBtn) +{ + fusionBtn->setToolTip(QString("Fusion")); + connect(fusionBtn, &QToolButton::clicked, this, [&] { + + ui->viewContainer->toggleViewWithFusion(); + + }); + + QMenu* m; + m = new QMenu(this); + m->addAction(tr("Reset Fusion"), this, [&] { + ui->viewContainer->removeCurrentViewWithFusion(); + }); + fusionBtn->setPopupMode(QToolButton::MenuButtonPopup); + fusionBtn->setMenu(m); +} + +void QDicomViewer::SetupSliceTool(QToolButton* sliceBtn) +{ + sliceBtn->setToolTip(QString("Browse series")); + connect(sliceBtn, &QToolButton::clicked, this, [&] { + //ui->viewContainer->setLeftButtonState(VTKIS_SLICE); + ui->viewContainer->SetInteractionMode(VTKIS_IMAGE_SLICING); + }); +} +void QDicomViewer::SetupPanTool(QToolButton* panBtn) +{ + panBtn->setToolTip(QString("Pan image")); + connect(panBtn, &QToolButton::clicked, this, [&] { + //ui->viewContainer->setLeftButtonState(VTKIS_PAN); + ui->viewContainer->SetInteractionMode(VTKIS_IMAGE_PAN); + }); +} +void QDicomViewer::SetupZoomTool(QToolButton* zoomBtn) +{ + zoomBtn->setToolTip(QString("Zoom image")); + connect(zoomBtn, &QToolButton::clicked, this, [&] { + //ui->viewContainer->setLeftButtonState(VTKIS_DOLLY); + ui->viewContainer->SetInteractionMode(VTKIS_IMAGE_ZOOM); + }); +} + +void QDicomViewer::SetupAdjustTool(QToolButton* winlevelBtn) +{ + winlevelBtn->setToolTip(QString("Adjust window level")); + connect(winlevelBtn, &QToolButton::clicked, this, [&] { + //ui->viewContainer->setLeftButtonState(VTKIS_WINDOW_LEVEL); + ui->viewContainer->SetInteractionMode(VTKIS_IMAGE_WINDOWLEVEL); + }); + + // Menu + QMenu* m; + //QAction *a; + m = new QMenu(this); + m->addAction(tr("Custom Window"), this, [=](bool value) { + winlevelBtn->setChecked(true); + if (nullptr == m_customwin) { + m_customwin = new Customwindow(this); + } + m_customwin->setCurrentView(ui->viewContainer->getCurrentView()); + //show custom window subwindow + m_customwin->setModal(true); + //m_customwin->show(); + m_customwin->exec(); + + }); + + m->addAction(tr("Negative"), this, [=](bool value) { + winlevelBtn->setChecked(true); + DicomImageView* curV = ui->viewContainer->getCurrentView(); + if (curV != nullptr && curV->HasSeries()) + { + curV->ToggleNegativeMode(); + } + }); + + winlevelBtn->setPopupMode(QToolButton::MenuButtonPopup); + winlevelBtn->setMenu(m); + +} + +void QDicomViewer::SetupEmptyTool(QToolButton* emptyBtn) +{ + emptyBtn->setToolTip(QString("Delete current series")); + connect(emptyBtn, &QToolButton::clicked, this, [&] { + ui->viewContainer->emptyCurrentView(); + }); +} + +void QDicomViewer::SetupFlipTool(QToolButton* flipBtn) +{ + flipBtn->setToolTip(QString("Transformations")); + QMenu* m; + m = new QMenu(this); + m->addAction(tr("Rotate 90 CCW"), this, [&] { + DicomImageView* curV = ui->viewContainer->getCurrentView(); + if (curV != nullptr && curV->HasSeries()) + { + curV->Rotate(90, ROTATE_90_CCW); + //curV->getImageViewer()->updateOrienInfo(); + } + + }); + m->addAction(tr("Rotate 90 CW"), this, [&] { + DicomImageView* curV = ui->viewContainer->getCurrentView(); + if (curV != nullptr && curV->HasSeries()) + { + curV->Rotate(-90, ROTATE_90_CW); + //curV->getImageViewer()->updateOrienInfo(); + } + }); + m->addAction(tr("Rotate 180"), this, [&] { + DicomImageView* curV = ui->viewContainer->getCurrentView(); + if (curV != nullptr && curV->HasSeries()) + { + curV->Rotate(180, ROTATE_180); + //curV->getImageViewer()->updateOrienInfo(); + } + + }); + m->addSeparator(); + m->addAction(tr("Flip horizontal"), this, [&] { + DicomImageView* curV = ui->viewContainer->getCurrentView(); + if (curV != nullptr && curV->HasSeries()) + { + curV->HFlip(); + //curV->getImageViewer()->updateOrienInfo(H_FLIP); + } + }); + m->addAction(tr("Flip vertical"), this, [&] { + DicomImageView* curV = ui->viewContainer->getCurrentView(); + if (curV != nullptr && curV->HasSeries()) + { + curV->VFlip(); + //curV->getImageViewer()->updateOrienInfo(V_FLIP); + } + + }); + m->addSeparator(); + m->addAction(tr("Clear transformations"), this, [&] { + DicomImageView* curV = ui->viewContainer->getCurrentView(); + if (curV != nullptr && curV->HasSeries()) + { + curV->ClearTransformations(); + //curV->getImageViewer()->updateOrienInfo(CLEAR); + } + }); + flipBtn->setPopupMode(QToolButton::MenuButtonPopup); + flipBtn->setMenu(m); + + connect(flipBtn, &QPushButton::clicked, this, [&] { + DicomImageView* curV = ui->viewContainer->getCurrentView(); + if (curV != nullptr && curV->HasSeries()) + { + curV->Rotate(90, ROTATE_90_CCW); + //curV->getImageViewer()->updateOrienInfo(ROTATE_90_CCW); + } + }); +} + +void QDicomViewer::SetupSyncTool(QToolButton* syncBtn) +{ + syncBtn->setToolTip(QString("Toggle series synchronization")); + // Menu + QMenu* m; + m = new QMenu(this); + + //QAction *a; + SyncState curst = SyncHelper::getSyncState(); + m_sync_state_action = m->addAction(QString(tr("CUR STATE: %1")).arg(SyncHelper::SyncStateName[curst]), this, [&](bool value) { + }); + m_sync_state_action->setCheckable(false); + + this->ui->toolBar->addSeparator(); + + m_sync_item_action[0] = m->addAction(tr("Sychronize slice position"), this, [&](bool value) { + SyncHelper::setSyncItem(SyncItem::SLICE_POS, value); + }); + m_sync_item_action[0]->setCheckable(true); + m_sync_item_action[0]->setChecked(false); + m_sync_item_action[0]->setDisabled(true); + + + m_sync_item_action[1] = m->addAction(tr("Sychronize zoom & pan"), this, [&](bool value) { + SyncHelper::setSyncItem(SyncItem::ZOOM_PAN, value); + }); + m_sync_item_action[1]->setCheckable(true); + m_sync_item_action[1]->setChecked(false); + m_sync_item_action[1]->setDisabled(true); + + + m_sync_item_action[2] = m->addAction(tr("Sychronize window level & width"), this, [&](bool value) { + SyncHelper::setSyncItem(SyncItem::WIDTH_LEVEL, value); + }); + m_sync_item_action[2]->setCheckable(true); + m_sync_item_action[2]->setChecked(false); + m_sync_item_action[2]->setDisabled(true); + syncBtn->setPopupMode(QToolButton::MenuButtonPopup); + syncBtn->setMenu(m); + + //loop click + connect(syncBtn, &QToolButton::clicked, this, [=] { + SyncState curst = SyncHelper::getSyncState(); + + switch (curst) + { + case AUTO_SYNC: + syncBtn->setIcon(icon_manual); + m_sync_state_action->setText(QString(tr("CUR STATE: %1")).arg(SyncHelper::SyncStateName[MANUAL_SYNC])); + + SyncHelper::setSyncState(MANUAL_SYNC); + m_sync_item_action[SyncItem::SLICE_POS]->setChecked(SyncHelper::getSyncItem(SyncItem::SLICE_POS)); + m_sync_item_action[SyncItem::WIDTH_LEVEL]->setChecked(SyncHelper::getSyncItem(SyncItem::WIDTH_LEVEL)); + m_sync_item_action[SyncItem::ZOOM_PAN]->setChecked(SyncHelper::getSyncItem(SyncItem::ZOOM_PAN)); + + //curst = MANUAL_SYNC; + + break; + case MANUAL_SYNC: + syncBtn->setIcon(icon_dis); + m_sync_state_action->setText(QString(tr("CUR STATE: %1")).arg(SyncHelper::SyncStateName[DIS_SYNC])); + + SyncHelper::setSyncState(DIS_SYNC); + for (int i = 0; i < SYNC_ITEM_NUM; i++) + { + m_sync_item_action[i]->setChecked(false); + m_sync_item_action[i]->setDisabled(true); + } + //curst = DIS_SYNC; + break; + case DIS_SYNC: + syncBtn->setIcon(icon_auto); + m_sync_state_action->setText(QString(tr("CUR STATE: %1")).arg(SyncHelper::SyncStateName[AUTO_SYNC])); + + SyncHelper::setSyncState(AUTO_SYNC); + for (int i = 0; i < SYNC_ITEM_NUM; i++) + { + m_sync_item_action[i]->setDisabled(false); + } + m_sync_item_action[SyncItem::SLICE_POS]->setChecked(SyncHelper::getSyncItem(SyncItem::SLICE_POS)); + m_sync_item_action[SyncItem::WIDTH_LEVEL]->setChecked(SyncHelper::getSyncItem(SyncItem::WIDTH_LEVEL)); + m_sync_item_action[SyncItem::ZOOM_PAN]->setChecked(SyncHelper::getSyncItem(SyncItem::ZOOM_PAN)); + //curst = AUTO_SYNC; + break; + default: + break; + } + + }); +} + +void QDicomViewer::SetupFileTool(QToolButton* fileBtn) { + // Menu + fileBtn->setToolTip(QString("Open Dicom series from directory")); + QMenu* m; + m = new QMenu(this); + m->addAction(tr("Open DICOM folder"), this, [&] { + QString p = QFileDialog::getExistingDirectory(this, tr("Open dicom directory"), m_qs.value("DIR_PATH_ID").toString()); + if (!p.isEmpty()) { + m_qs.setValue("DIR_PATH_ID", p); + drawDICOM(p.toStdString(), DIR_OPEN_MODE); + } + }); + + m->addAction(tr("Open DICOM file"), this, [&] { + //QSettings s; + //QString p = s.value(FILE_PATH_ID).toString(); + QString fn = QFileDialog::getOpenFileName(this, tr("Open dicom files"), m_qs.value("FILE_PATH_ID").toString()); + if (!fn.isEmpty()) { + m_qs.setValue("FILE_PATH_ID", fn); + drawDICOM(fn.toStdString(), FILE_OPEN_MODE); + } + }); + + m->addAction(tr("Close all"), this, [&] { + //ui->thumbnailBar_->clear(); + }); + + m->addSeparator(); + m->addAction(tr("Quit"), this, &QDicomViewer::close); + fileBtn->setPopupMode(QToolButton::MenuButtonPopup); + fileBtn->setMenu(m); + + // connect + connect(fileBtn, &QToolButton::clicked, this, [&] { + //QSettings s; + //QString p = s.value(DIR_PATH_ID, ".").toString(); + QString p = QFileDialog::getExistingDirectory(this, tr("Open dicom directory"), m_qs.value("DIR_PATH_ID").toString()); + if (!p.isEmpty()) { + m_qs.setValue("DIR_PATH_ID", p); + //ui->thumbnailBar_->setImagePaths(QStringList() << p); + drawDICOM(p.toStdString(), DIR_OPEN_MODE); + } + }); +} + +void QDicomViewer::SetupImportTool(QToolButton* importBtn) +{ + importBtn->setToolTip(QString("Search and download studies from PACS locations")); + connect(importBtn, &QToolButton::clicked, this, [&] { + if (nullptr == m_import) { + m_import = new ImportWidget(this); + connect(m_import, &ImportWidget::sigMoveDone, this, &QDicomViewer::openDICOMFromPACS); + } + m_import->show(); + }); + + +} + +void QDicomViewer::SetupExportTool(QToolButton* saveBtn) +{ + saveBtn->setToolTip(QString("Export images")); + connect(saveBtn, &QToolButton::clicked, this, [=] { + if (nullptr == exportDialog) { + exportDialog = new ExportDialog(this); + //exportDialog->setViewContainer(ui->viewContainer); + exportDialog->setCurView(ui->viewContainer->getCurrentView()); + exportDialog->setModal(false); + } + exportDialog->show(); + + }); +} + +void QDicomViewer::displayThumbnailBar(bool value) +{ + VCRHelper::setThumbnailbar(value); + ui->thumbnailBar->setVisible(value); + m_preview_display_action->setChecked(value); +} + +void QDicomViewer::SetupGridTool(QToolButton* gridBtn) { + // Menu + //qDebug() << &gridBtn; + gridBtn->setToolTip(QString("Split Screen")); + QMenu* m; + m = new QMenu(this); + m_preview_display_action = m->addAction(tr("Preview bar"), this, [&](bool value) { + displayThumbnailBar(value); + + }); + m_preview_display_action->setCheckable(true); + m_preview_display_action->setChecked(false); + + gridBtn->setPopupMode(QToolButton::MenuButtonPopup); + gridBtn->setMenu(m); + // connect + connect(gridBtn, &QToolButton::clicked, this, [=] { + // auto delete while hidden + //qDebug() << &gridBtn; + GridPopWidget* gpw = new GridPopWidget(gridBtn); + connect(gpw, &GridPopWidget::Signal_ViewLayout, + ui->viewContainer, &ViewContainerWidget::Slot_SetViewLayout); + gpw->move(this->geometry().topLeft() + gridBtn->geometry().bottomLeft() + QPoint(25, 25)); + gpw->show(); + }); +} + + +void QDicomViewer::setConnections() +{ + //connect(ui->action_OpenDicomFile, SIGNAL(triggered()), this, SLOT(OnOpenDicomFile())); + //connect(ui->action_OpenDicomFolder, SIGNAL(triggered()), this, SLOT(OnOpenSeriesFolder())); + //connect(ui->action_ShowMetaData, SIGNAL(triggered()), this, SLOT(OnShowMetaData())); + //connect(ui->action_ExportImage, SIGNAL(triggered()), this, SLOT(OnExportImage())); + //connect(ui->action_ImportImage, SIGNAL(triggered()), this, SLOT(OnImportImage())); + //connect(ui->action_Fusion, SIGNAL(triggered()), this, SLOT(OnFusion())); + + connect(ui->viewContainer, SIGNAL(Signal_NotifyThumbnail(DicomImageView*)), + ui->thumbnailBar, SLOT(Slot_setCurrentThumbnail(DicomImageView*))); + + connect(ui->thumbnailBar, SIGNAL(Signal_ThumbClicked(thumbnailImage*)), + ui->viewContainer, SLOT(Slot_ThumbnailClickEvent(thumbnailImage*))); + + connect(ui->toolBar, SIGNAL(visibilityChanged(bool)), + this, SLOT(Slot_ToolbarVisibilityChanged(bool))); + + + + //connect(ui->viewContainer, SIGNAL(Signal_NotifyThumbnail(DicomImageView*)), + // ui->vcr_toolbar, SLOT(setImageView(DicomImageView*))); + + //connect(ui->thumbnailBar, SIGNAL(Signal_CopyDicomView(SeriesInfo_t*)), + // this, SLOT(Slot_CopyDicomView(SeriesInfo_t*))); +} + + + +void QDicomViewer::Slot_ToolbarVisibilityChanged(bool visible) +{ + VCRHelper::setToolbar(visible); +} + +void QDicomViewer::openDICOMFromPACS(int err, std::string dirName) +{ + if (err == NOERROR) { + drawDICOM(dirName, DIR_OPEN_MODE); + } + else + { + //pop out msg box + QMessageBox::warning(this, "Warning", "open DICOM Images From PACS Fail"); + } +} + +void QDicomViewer::drawDICOM(const std::string& dicomName, SeriesOpenMode openMode) +{ + + displayThumbnailBar(true); + + + DicomLoader* helper = DicomLoader::GetInstance(); + helper->setDirObservers(this); + + //call must in order! + //1.come first + helper->ItkPreReadSeries(dicomName, openMode); + + //2.using gdcm + UniqueIDInfo_t* unique = helper->createUniqueID(dicomName, openMode); + + //When IsDuplicate()is called, AddDicomType is determined! + //You have to add series before delete, or encounter AddDicomType inconsistency problem! + if (helper->IsDuplicate(unique)) + { + this->statusBar()->showMessage(tr("Already Exists!")); + + //need to delete UniqueID first + //delete unique; + return; + } + + + DicomTagInfo_t* tag_info = helper->createDicomTagsInfo(); + + + //You have to judge AddDicomType first! + AddDicomType type = helper->getAddDicomType(); + + DicomImageView* curV = nullptr; + if (type == AddDicomType::OVERRIDE_LEVEL) + { + /************************************************************************/ + /* 1.Get multiple relevant windows + * 2.createSeries and addSeries one by one + * 3.Get the return replaced instance,delete it! + /************************************************************************/ + QList view_list; + ui->viewContainer->getRelevantViewList(*unique, view_list); + + //only the first conducts raw mode + //the following conducts copy mode + bool copy = false; + //UniqueIDInfo_t *d_unique; + //DicomTagInfo_t*d_tag_info; + + for (DicomImageView* v : view_list) + { + //copy mode + SeriesInstance* series = helper->createSeries(unique, tag_info, v->getRenWin(), copy); + //after series is properly set, call addSeriesInstance + SeriesInstance* to_del = helper->addSeriesInstance(series); + if (false == copy) + { + ui->thumbnailBar->updateThumbnailBar(); + //d_unique = to_del->getUniqueID(); + //d_tag_info = to_del->getDicomTagInfo(); + copy = true; + } + v->setDicomImageView(series); + v->Render(); + // not use deleteSeries here + delete to_del; + //curV = v; + } + //it is time to release memory of series + //delete d_unique; + //delete d_tag_info; + curV = view_list.at(view_list.size() - 1); + ui->viewContainer->setCurrentView(curV); + } + else + { + //Note: you dont have to deal with the replaced instance,let it go + curV = ui->viewContainer->getCurrentView(); + + SeriesInstance* old = nullptr; + if (curV->HasSeries()) + { + old = curV->getSeriesInstance(); + } + ui->viewContainer->replaceViewWithSerie(unique, tag_info, curV, old); + //view clicked will notify thumbnailbar update + ui->thumbnailBar->updateThumbnailBar(); + ui->viewContainer->setCurrentView(curV); + } + +} + + diff --git a/src/src/base/DicomLoader.cpp b/src/src/base/DicomLoader.cpp new file mode 100644 index 0000000..235b7ab --- /dev/null +++ b/src/src/base/DicomLoader.cpp @@ -0,0 +1,748 @@ +#include "base/DicomLoader.h" +#include +#include +#include +#include "base/seriesinstance.h" +#include +#include +#include +#include + +#define ORIEN_NUM 4 +#define SINGLE_INSTANCE 1 +OrienMapType OrienHelper::orien_map; + +/************************************************************************ +* [Q]Is there anything different between these two? +* instance->m_image = m_itkConnector->GetOutput(); +* instance->m_image->DeepCopy(m_itkConnector->GetOutput()); +* [Q]I have no idea why it works just fine not using deep copy, + while the vtkdata is replaced in the Qfusion Project using exatly the same workflow! +/************************************************************************/ +std::string OrienHelper::StringFilter(char* str) +{ + const char s[] = "\\ "; + char *token; + char *buftmp; + token = strtok_s(str, s, &buftmp); + std::string trim =""; + while (token != NULL) { + //printf("%s\n", token); + trim += token; + token = strtok_s(NULL, s, &buftmp); + } + return trim; +} + +void OrienHelper::init() +{ + const std::string index[] = { "LH","LF","RH","RF" }; + std::vector indexList(std::begin(index), std::end(index)); + + const std::string lh[] = { "S" ,"L" ,"R" ,"I" }; + const std::string lf[] = { "I" , "L" , "R" , "S" }; + const std::string rh[] = { "S" , "R" , "L" , "I" }; + const std::string rf[] = { "I" , "R" , "L" , "S" }; + + std::vector v_lh(std::begin(lh), std::end(lh)); + std::vector v_lf(std::begin(lf), std::end(lf)); + std::vector v_rh(std::begin(rh), std::end(rh)); + std::vector v_rf(std::begin(rf), std::end(rf)); + + std::vector> retList; + retList.push_back(v_lh); + retList.push_back(v_lf); + retList.push_back(v_rh); + retList.push_back(v_rh); + + for (int i = 0; i < ORIEN_NUM; i++) + { + OrienHelper::orien_map.insert(std::pair>(indexList.at(i),retList.at(i))); + } +} + +std::vector* OrienHelper::getOrienStrList(const std::string &index) +{ + + const char* s = index.c_str(); + int len = strlen(s); + char *hint = new char[len + 1]; + strcpy_s(hint, len + 1, s); + + std::string trim = StringFilter(hint); + OrienMapType::iterator it = OrienHelper::orien_map.find(trim); + delete hint; + + if (it == orien_map.end()) + { + + return nullptr; + } + return &(it->second); +} + + +DicomLoader* DicomLoader::instance = new DicomLoader(); +DicomLoader* DicomLoader::GetInstance() { + //lazy man + //if (!instance) { + // instance = new DicomLoader(); + //} + //hungry man + return instance; +} + +DicomLoader::DicomLoader():m_addType(AddDicomType::DUPLICATE_TYPE) { + + m_itkSeriesReader = SeriesReaderType::New(); + m_itkConnector = ConnectorType::New(); + m_gdcmIO = ImageIOType::New(); + m_inputNames = InputNamesGeneratorType::New(); + + //transfer to series after! + m_patients.clear(); + +} +DicomLoader::~DicomLoader() { + delete instance; +} + + +bool DicomLoader::IsDuplicate(UniqueIDInfo_t* unique) +{ + + if (m_patients.count(unique->patient_name) == 0) + { + //new patient + m_addType = AddDicomType::PATINET_LEVEL; + return false; + } + + PatientsMapType::iterator cur_patient_iter = m_patients.find(unique->patient_name); + StudiesMapType *studies = cur_patient_iter->second->studies; + + if (studies->count(unique->study_uid) == 0) + { + //new study + m_addType = AddDicomType::STUDY_LEVEL; + return false; + } + + + StudiesMapType::iterator cur_study_iter = studies->find(unique->study_uid); + SeriesMapType *series = cur_study_iter->second->series; + if (series->count(unique->series_uid) == 0) + { + //new series + m_addType = AddDicomType::SERIES_LEVEL; + return false; + } + + SeriesMapType::iterator cur_series_iter = series->find(unique->series_uid); + + //if (cur_series_iter->second->getSerirsOpenMode()== FILE_OPEN_MODE && + if (cur_series_iter->second->open_mode == FILE_OPEN_MODE && + unique->open_mode == DIR_OPEN_MODE) + { + m_addType = AddDicomType::OVERRIDE_LEVEL; + return false; + } + + if (cur_series_iter->second->open_mode == FILE_OPEN_MODE && + unique->open_mode == FILE_OPEN_MODE) + { + if (cur_series_iter->second->instance_num != unique->instance_num) + { + m_addType = AddDicomType::OVERRIDE_LEVEL; + return false; + } + } + + m_addType = AddDicomType::DUPLICATE_TYPE; + return true; +} + + + + +void DicomLoader::InitFromCopy(SeriesInstance* instance) +{ + //copy data from existing instance! + UniqueIDInfo_t* unique = instance->getUniqueID(); + //SeriesInfo_t* serie_info = getSerieInfo(*unique); + SeriesInstance *exists = getFirstInstance(*unique); + //DicomTagInfo_t *tag_info = serie_info->tag_info; + //instance->setDicomTagInfo(exists->getDicomTagInfo()); + + //copyDicomTagsInfo(exists, instance); + //smart pointer reference count++ + instance->m_image = exists->m_image; + + if (exists->getUniqueID()->open_mode == DIR_OPEN_MODE) + { + //vector deep copy + //exists->m_fileNames = instance->m_fileNames;This is a bug! + instance->m_fileNames = exists->m_fileNames; + } +} + +void DicomLoader::InitFromRead(SeriesInstance* instance)//, DicomTagInfo_t* tag_info) +{ + + ////////////////////////////////////////////////////////////////////////// + //You have to use DeepCopy on this condition,otherwise, the previous image will be flushed. + instance->m_image->DeepCopy(m_itkConnector->GetOutput()); + //instance->m_image = m_itkConnector->GetOutput(); + + if (instance->getUniqueID()->open_mode == DIR_OPEN_MODE) + { + instance->m_fileNames = m_inputNames->GetInputFileNames(); + } +} + +//You may not use const & cause intance may be deleted during this func +//delete all the instances assotiated with this series UID +void DicomLoader::deleteInstanceFromMap(UniqueIDInfo uniqueID) +{ + //const UniqueIDInfo & uniqueID = instance->getUniqueID(); + + PatientsMapType::iterator cur_patient_iter = m_patients.find(uniqueID.patient_name); + + StudiesMapType::iterator cur_study_iter = cur_patient_iter->second->studies->find(uniqueID.study_uid); + + //in order + SeriesInfo_t* the_series = cur_study_iter->second->series->at(uniqueID.series_uid); + //SeriesInstance *cur_series = cur_study_iter->second->series->at(uniqueID.series_uid); + + + //delete all the instances assotiated with this series UID + for (int i = 0; i < the_series->instances->size(); i++) + { + delete the_series->instances->at(i); + } + the_series->instances->clear(); + + + //erase series from study + cur_study_iter->second->series->erase(uniqueID.series_uid); + + //Recursively Delete + + //if the study has no series + if (cur_study_iter->second->series->empty()) + { + cur_patient_iter->second->studies->erase(uniqueID.study_uid); + } + + //if the patient has no study + if (cur_patient_iter->second->studies->empty()) + { + m_patients.erase(uniqueID.patient_name); + } + + //cause you need uniqueID, therefore you have to delete series at last + //delete cur_series; +} +InstancesVecType* DicomLoader::getInstancesVec(const UniqueIDInfo &uniqueID) +{ + PatientsMapType::iterator cur_patient_iter = m_patients.find(uniqueID.patient_name); + if (cur_patient_iter == m_patients.end()) + { + return nullptr; + } + + StudiesMapType::iterator cur_study_iter = cur_patient_iter->second->studies->find(uniqueID.study_uid); + if (cur_study_iter == cur_patient_iter->second->studies->end()) + { + return nullptr; + } + + SeriesMapType::iterator the_series = cur_study_iter->second->series->find(uniqueID.series_uid); + if (the_series == cur_study_iter->second->series->end()) + { + return nullptr; + } + + return the_series->second->instances; + +} + +void DicomLoader::getOpenedInstancesVec(InstancesVecType& retVec) +{ + for (PatientsMapType::const_iterator cur_patient_iter = m_patients.begin(); cur_patient_iter != + m_patients.end(); cur_patient_iter++) + { + StudiesMapType *cur_studies = cur_patient_iter->second->studies; + for (StudiesMapType::const_iterator cur_study_iter = cur_studies->begin(); cur_study_iter != + cur_studies->end(); cur_study_iter++) + { + SeriesMapType *cur_series = cur_study_iter->second->series; + for (SeriesMapType::const_iterator cur_series_iter = cur_series->begin(); cur_series_iter != + cur_series->end(); cur_series_iter++) + { + //default export instance: the recently added one + SeriesInstance *last = cur_series_iter->second->instances->back(); + if(nullptr!=last) + { + retVec.push_back(last); + } + } + } + + } +} + +SeriesInstance* DicomLoader::getFirstInstance(const UniqueIDInfo &uniqueID) +{ + PatientsMapType::iterator cur_patient_iter = m_patients.find(uniqueID.patient_name); + if (cur_patient_iter == m_patients.end()) + { + return nullptr; + } + + StudiesMapType::iterator cur_study_iter = cur_patient_iter->second->studies->find(uniqueID.study_uid); + if (cur_study_iter == cur_patient_iter->second->studies->end()) + { + return nullptr; + } + + SeriesMapType::iterator the_series = cur_study_iter->second->series->find(uniqueID.series_uid); + if (the_series == cur_study_iter->second->series->end()) + { + return nullptr; + } + + return *the_series->second->instances->begin(); + +} +SeriesInfo_t* DicomLoader::getSerieInfo(const UniqueIDInfo &uniqueID) +{ + PatientsMapType::iterator cur_patient_iter = m_patients.find(uniqueID.patient_name); + if (cur_patient_iter == m_patients.end()) + { + return nullptr; + } + + StudiesMapType::iterator cur_study_iter = cur_patient_iter->second->studies->find(uniqueID.study_uid); + if (cur_study_iter == cur_patient_iter->second->studies->end()) + { + return nullptr; + } + + SeriesMapType::iterator the_series = cur_study_iter->second->series->find(uniqueID.series_uid); + if (the_series == cur_study_iter->second->series->end()) + { + return nullptr; + } + + return the_series->second; +} + +bool DicomLoader::deleteSeriesInstance(SeriesInstance* old) +{ + //DicomLoader *helper = DicomLoader::GetInstance(); + InstancesVecType* inst_vec = instance->getInstancesVec(*old->getUniqueID()); + + //if there is only one instance,do not delete + if (inst_vec->size() == SINGLE_INSTANCE) + { + //Comment1:You should set Series Instance to no DicomImageView + //Comment2:It is hard to tell wether it is in use,you'd better do nothing + //old->orphanizeSeriesInstance(); + //You should set Series Instance to no DicomImageView + //old->orphanizeSeriesInstance(); + return false; + } + + for (InstancesVecType::iterator iter = inst_vec->begin(); iter != inst_vec->end(); iter++) + { + if (old == *iter) + { + inst_vec->erase(iter); + delete old; + return true; + } + } + return false; +} + +SeriesInstance* DicomLoader::addSeriesInstance(SeriesInstance* instance,bool copy) +{ + DicomTagInfo_t* seriesInfo = instance->getDicomTagInfo(); + UniqueIDInfo_t* unique = instance->getUniqueID(); + + InstancesVecType* all_instances = nullptr; + SeriesMapType* all_series = nullptr; + StudiesMapType* all_studies = nullptr; + + SeriesInfo_t* series = nullptr; + StudyInfo_t* study = nullptr; + PatientInfo_t *patient = nullptr; + PatientsMapType::iterator cur_patient_iter; + StudiesMapType::iterator cur_study_iter; + SeriesMapType::iterator cur_series_iter; + + + if (copy) + { + //just push back + cur_patient_iter = m_patients.find(unique->patient_name); + all_studies = cur_patient_iter->second->studies; + cur_study_iter = all_studies->find(unique->study_uid); + cur_series_iter = cur_study_iter->second->series->find(unique->series_uid); + all_instances = cur_series_iter->second->instances; + all_instances->push_back(instance); + return nullptr; + } + + switch (m_addType) + { + case DUPLICATE_TYPE: + break; + case PATINET_LEVEL: + all_instances = new InstancesVecType(); + all_instances->push_back(instance); + + series = new SeriesInfo_t(); + series->open_mode = unique->open_mode; + if (unique->open_mode == FILE_OPEN_MODE) + { + series->instance_num = unique->instance_num; + } + //EEROR CODE HERE,YOU CAN NOT DEPEND ON SINGLE INSTANCE!! + series->tag_info = instance->getDicomTagInfo(); + series->unique_info = instance->getUniqueID(); + series->series_pixmap = instance->GetPixmap(); + series->instances = all_instances; + + all_series = new SeriesMapType(); + all_series->insert(std::pair(unique->series_uid, series)); + + //patient + all_studies = new StudiesMapType(); + study = new StudyInfo_t(); + study->study_description = seriesInfo->m_StudyDescription; + study->study_date = seriesInfo->m_StudyDate; + study->study_time = seriesInfo->m_StudyTime; + study->series = all_series; + all_studies->insert(std::pair(unique->study_uid, study)); + + //patient level + patient = new PatientInfo_t(); + patient->patient_name = seriesInfo->m_PatientName; + patient->birth_date = seriesInfo->m_PatientBirth; + patient->studies = all_studies; + m_patients.insert(std::pair(unique->patient_name, patient)); + break; + case STUDY_LEVEL: + all_instances = new InstancesVecType(); + all_instances->push_back(instance); + + series = new SeriesInfo_t(); + series->open_mode = unique->open_mode; + if (unique->open_mode == FILE_OPEN_MODE) + { + series->instance_num = unique->instance_num; + } + series->tag_info = instance->getDicomTagInfo(); + series->unique_info = instance->getUniqueID(); + series->series_pixmap = instance->GetPixmap(); + series->instances = all_instances; + + //study + all_series = new SeriesMapType(); + all_series->insert(std::pair(unique->series_uid, series)); + + study = new StudyInfo_t(); + study->study_description = seriesInfo->m_StudyDescription; + study->study_date = seriesInfo->m_StudyDate; + study->study_time = seriesInfo->m_StudyTime; + study->series = all_series; + + cur_patient_iter = m_patients.find(unique->patient_name); + all_studies = cur_patient_iter->second->studies; + all_studies->insert(std::pair(unique->study_uid, study)); + + break; + case SERIES_LEVEL: + all_instances = new InstancesVecType(); + all_instances->push_back(instance); + + series = new SeriesInfo_t(); + series->open_mode = unique->open_mode; + if (unique->open_mode == FILE_OPEN_MODE) + { + series->instance_num = unique->instance_num; + } + series->tag_info = instance->getDicomTagInfo(); + series->unique_info = instance->getUniqueID(); + series->series_pixmap = instance->GetPixmap(); + series->instances = all_instances; + + cur_patient_iter = m_patients.find(unique->patient_name); + all_studies = cur_patient_iter->second->studies; + cur_study_iter = all_studies->find(unique->study_uid); + //all_series = new SeriesMapType(); + all_series = cur_study_iter->second->series; + all_series->insert(std::pair(unique->series_uid, series)); + + break; + + //combine two situationas, both image and series + case OVERRIDE_LEVEL: + cur_patient_iter = m_patients.find(unique->patient_name); + all_studies = cur_patient_iter->second->studies; + cur_study_iter = all_studies->find(unique->study_uid); + + cur_series_iter = cur_study_iter->second->series->find(unique->series_uid); + + cur_series_iter->second->open_mode = unique->open_mode; + if (unique->open_mode == FILE_OPEN_MODE) + { + cur_series_iter->second->instance_num = unique->instance_num; + } + //need to delete + cur_series_iter->second->tag_info = instance->getDicomTagInfo(); + cur_series_iter->second->unique_info = instance->getUniqueID(); + + cur_series_iter->second->series_pixmap = instance->GetPixmap(); + //wait for thumbnail update to delete it... + cur_series_iter->second->pixmap_valid = false; + all_instances = cur_series_iter->second->instances; + + for (int i = 0; i < all_instances->size(); i++) + { + //check if it is newly created or old to delete + SeriesInstance *cur_series = all_instances->at(i); + if (cur_series->getUniqueID() != instance->getUniqueID()) + { + //return the replaced pointer + all_instances->at(i) = instance; + return cur_series; + } + + } + + /*what if the images is shown*/ + //remove the duplicate series, replace it with new. + //do this in delete stage + //last = cur_study_iter->second->series->at(unique->series_uid); + //delete last; + //cur_study_iter->second->series->erase(unique->series_uid); + + //cur_study_iter->second->series->insert(std::pair(m_uniqueID.series_uid, instance)); + break; + default: + break; + } + return nullptr; +} + +void DicomLoader::ItkPreReadSeries(const std::string &dicomName, SeriesOpenMode openMode) +{ + //m_itkSeriesReader->SetImageIO(gdcmIO); + m_itkSeriesReader->SetImageIO(m_gdcmIO); + if (openMode == FILE_OPEN_MODE) + { + //m_itkSeriesReader->SetFileName(m_dicomName.toStdString()); + m_itkSeriesReader->SetFileName(dicomName); + } + if (openMode == DIR_OPEN_MODE) + { + + //Read the input series + m_inputNames->SetInputDirectory(dicomName.c_str()); + const SeriesReaderType::FileNamesContainer & filenames = m_inputNames->GetInputFileNames(); + + //m_itkSeriesReader->SetFileNames(filenames); + m_itkSeriesReader->SetFileNames(filenames); + } + + try { + //m_itkSeriesReader->Update(); + m_itkSeriesReader->UpdateLargestPossibleRegion(); + m_itkSeriesReader->Update(); + } + catch (itk::ExceptionObject &excp) { + std::cerr << "Exception thrown while reading the series" << std::endl; + std::cerr << excp << std::endl; + } + + //Connector to convert ITK image data to VTK image data + //connector->SetInput(m_itkSeriesReader->GetOutput()); //Set ITK reader Output to connector you can replace it with filter + m_itkConnector->SetInput(m_itkSeriesReader->GetOutput()); + try { //Exceptional handling + m_itkConnector->Update(); + } + catch (itk::ExceptionObject & e) { + std::cerr << "exception in file reader " << std::endl; + std::cerr << e << std::endl; + } + +} + + +UniqueIDInfo_t* DicomLoader::createUniqueID(const std::string &dicomName, SeriesOpenMode openMode) +{ + UniqueIDInfo_t* pUniqueID(new UniqueIDInfo_t()); + + + pUniqueID->open_mode = openMode; + pUniqueID->dicom_name = dicomName; + if (openMode == FILE_OPEN_MODE) + { + m_gdcmIO->GetValueFromTag(USER_CONFIG::TAG_INSTANCE_NUM, pUniqueID->instance_num); + } + m_gdcmIO->GetValueFromTag(USER_CONFIG::TAG_PATIENT_NAME, pUniqueID->patient_name); + m_gdcmIO->GetValueFromTag(USER_CONFIG::TAG_STUDY_UID, pUniqueID->study_uid); + m_gdcmIO->GetValueFromTag(USER_CONFIG::TAG_SERIES_UID, pUniqueID->series_uid); + + return pUniqueID; +} + + +void DicomLoader::setDirObservers(void* client) +{ + //Adding a reading progress observer to the reader so we can see how are we reading. + itk::CStyleCommand::Pointer pcl = itk::CStyleCommand::New(); + pcl->SetCallback((itk::CStyleCommand::FunctionPointer)&itkReaderProCallbackFunction_DIR); + pcl->SetClientData(client); + //m_itkSeriesReader->AddObserver(itk::ProgressEvent(), pcl); + m_itkSeriesReader->AddObserver(itk::ProgressEvent(), pcl); + + itk::CStyleCommand::Pointer pcl2 = itk::CStyleCommand::New(); + pcl2->SetClientData(client); + pcl2->SetCallback((itk::CStyleCommand::FunctionPointer)&itkReaderEndCallbackFunction); + //m_itkSeriesReader->AddObserver(itk::EndEvent(), pcl2); + m_itkSeriesReader->AddObserver(itk::EndEvent(), pcl2); + + +} +void DicomLoader::setFileObservers(void* client) +{ + //Adding a reading progress observer to the reader so we can see how are we reading. + itk::CStyleCommand::Pointer pcl = itk::CStyleCommand::New(); + pcl->SetCallback((itk::CStyleCommand::FunctionPointer)&itkReaderProCallbackFunction_FILE); + pcl->SetClientData(client); + //m_itkSeriesReader->AddObserver(itk::ProgressEvent(), pcl); + m_itkSeriesReader->AddObserver(itk::ProgressEvent(), pcl); + + itk::CStyleCommand::Pointer pcl2 = itk::CStyleCommand::New(); + pcl2->SetClientData(client); + pcl2->SetCallback((itk::CStyleCommand::FunctionPointer)&itkReaderEndCallbackFunction); + //m_itkSeriesReader->AddObserver(itk::EndEvent(), pcl2); + m_itkSeriesReader->AddObserver(itk::EndEvent(), pcl2); +} + + +void DicomLoader::itkReaderProCallbackFunction_FILE(itk::ProcessObject* obj, const itk::ProgressEvent&, void* data) +{ + QMainWindow* qdv = reinterpret_cast(data); + QString status = QString("Reading Images..."); + qdv->statusBar()->showMessage(status); +} + +void DicomLoader::itkReaderProCallbackFunction_DIR(itk::ProcessObject* obj, const itk::ProgressEvent&, void* data) +{ + QMainWindow* qdv = reinterpret_cast(data); + QString status = QString("Scanning Folder...(%1%)").arg(100 * obj->GetProgress()); + qdv->statusBar()->showMessage(status); +} + +void DicomLoader::itkReaderEndCallbackFunction(itk::ProcessObject* obj, const itk::ProgressEvent&, void* data) + +{ + QMainWindow* qdv = reinterpret_cast(data); + qdv->statusBar()->showMessage("Ready"); +} +// +//void DicomLoader::copyDicomTagsInfo(SeriesInstance* origin, SeriesInstance* copy) +//{ +// const DicomTagInfo_t *origin_info = origin->getConstDicomTagInfo(); +// DicomTagInfo_t *copy_info = copy->getDicomTagInfo(); +// copy_info->m_PatientName = origin_info->m_PatientName; +// copy_info->m_StudyDescription = origin_info->m_StudyDescription; +// copy_info->m_Institution = origin_info->m_Institution; +// copy_info->m_StudyDate = origin_info->m_StudyDate; +// copy_info->m_Modality = origin_info->m_Modality; +// copy_info->lbl_ser_num = origin_info->lbl_ser_num; +// copy_info->m_SeriesNumber = origin_info->m_SeriesNumber; +// copy_info->m_SeriesDescription = origin_info->m_SeriesDescription; +// copy_info->m_StudyTime = origin_info->m_StudyTime; +// copy_info->m_PatientBirth = origin_info->m_PatientBirth; +// copy_info->m_SliceNumber = origin_info->m_SliceNumber; +// copy_info->m_orientation = origin_info->m_orientation; +// copy_info->WL = origin_info->WL; +// copy_info->WW = origin_info->WW; +// +//} +DicomTagInfo_t* DicomLoader::createDicomTagsInfo() +{ + + DicomTagInfo_t* info = new DicomTagInfo_t(); + + char PatientName[255]; + char StudyDescription[255]; + char Institution[255]; + char StudyDate[255]; + char Modality[255]; + + std::string s_wl; + std::string s_ww; + + for (int i = 0; i < 3; i++) + { + info->spacing[i] = m_gdcmIO->GetSpacing(i); + } + + m_gdcmIO->GetPatientName(PatientName); + info->m_PatientName = std::string(PatientName); + m_gdcmIO->GetStudyDescription(StudyDescription); + info->m_StudyDescription = std::string(StudyDescription); + m_gdcmIO->GetInstitution(Institution); + info->m_Institution = std::string(Institution); + m_gdcmIO->GetStudyDate(StudyDate); + info->m_StudyDate = std::string(StudyDate); + m_gdcmIO->GetStudyDate(Modality); + info->m_Modality = std::string(Modality); + m_gdcmIO->GetValueFromTag(USER_CONFIG::TAG_WINDOW_LEVEL, s_wl); + m_gdcmIO->GetValueFromTag(USER_CONFIG::TAG_WINDOW_WIDTH, s_ww); + itk::GDCMImageIO::GetLabelFromTag(USER_CONFIG::TAG_SERIES_NUMBER, info->lbl_ser_num); + m_gdcmIO->GetValueFromTag(USER_CONFIG::TAG_SERIES_NUMBER, info->m_SeriesNumber); + m_gdcmIO->GetValueFromTag(USER_CONFIG::TAG_SERIES_DESCRIPTION, info->m_SeriesDescription); + m_gdcmIO->GetValueFromTag(USER_CONFIG::TAG_STUDY_TIME, info->m_StudyTime); + m_gdcmIO->GetValueFromTag(USER_CONFIG::TAG_PATIANT_BIRTH, info->m_PatientBirth); + + if (m_inputNames) { + info->m_SliceNumber = to_string(m_inputNames->GetInputFileNames().size()); + } + else + { + m_gdcmIO->GetValueFromTag(USER_CONFIG::TAG_SLICE_NUM, info->m_SliceNumber); + } + + m_gdcmIO->GetValueFromTag(USER_CONFIG::TAG_PATIENT_ORIENTATION,info->m_orientation); + info->WL = std::atoi(s_wl.c_str()); + info->WW = std::atoi(s_ww.c_str()); + return info; +} + +SeriesInstance* DicomLoader::createSeries(UniqueIDInfo_t* uniqueID, DicomTagInfo_t* tag_info, vtkGenericOpenGLRenderWindow* gl_rewin, bool copy) +{ + + SeriesInstance* series = new SeriesInstance(uniqueID, tag_info, gl_rewin); + //DicomLoader *helper = DicomLoader::GetInstance(); + /******in order*******/ + //whether create or copy from existing one + if (copy) + { + instance->InitFromCopy(series); + } + else + { + instance->InitFromRead(series); + } + series->setUpSeriesInstance(); + return series; +} + diff --git a/src/src/base/dicomviewerhelper.cpp b/src/src/base/dicomviewerhelper.cpp new file mode 100644 index 0000000..e4392dc --- /dev/null +++ b/src/src/base/dicomviewerhelper.cpp @@ -0,0 +1,181 @@ +锘#include "base/dicomviewerhelper.h" +#include +#include + + +//---------------------------------------------------------------------------------- +DicomViewerProductCfg::DicomViewerProductCfg() + : m_bLoaded(false) +{ +} + +DicomViewerProductCfg::~DicomViewerProductCfg() +{ +} + +bool DicomViewerProductCfg::loadcfg() +{ + if(m_bLoaded) + return true; + QString strProductFileName = DicomViewerHelper::applicationPath(); + strProductFileName += "/cfg/pacs.json"; + + QFile file(strProductFileName); + if(!file.open(QFile::ReadOnly)) + return false; + + QJsonParseError JsonParserError; + m_JsonDocument = QJsonDocument::fromJson(file.readAll(), &JsonParserError); + if(JsonParserError.error != QJsonParseError::NoError) + return false; + + m_JsonRootObject = m_JsonDocument.object(); + file.close(); + m_bLoaded = true; + return true; +} + +bool DicomViewerProductCfg::savecfg() +{ + if(!m_bLoaded) + return false; + + QString strProductFileName = DicomViewerHelper::applicationPath(); + strProductFileName += "/cfg/pacs.json"; + QFile file(strProductFileName); + if(!file.open(QFile::WriteOnly|QFile::Text|QFile::Truncate)) + return false; + + m_JsonDocument.setObject(m_JsonRootObject); + file.write(m_JsonDocument.toJson()); + file.close(); + return true; +} + +QString DicomViewerProductCfg::ourTitle() +{ + if(!loadcfg()) + return QString(); + + QJsonValueRef ref = m_JsonRootObject.find("listener").value(); + return (ref.toObject())["ae"].toString(); +} + +bool DicomViewerProductCfg::setOurTitle(QString ae) +{ + if(!loadcfg()) + return false; + + QJsonValueRef ref = m_JsonRootObject.find("listener").value(); + QJsonObject obj = ref.toObject(); + obj["ae"] = ae; + ref = obj; + return savecfg(); +} + +QString DicomViewerProductCfg::ourPort() +{ + if (!loadcfg()) + return QString(); + + QJsonValueRef ref = m_JsonRootObject.find("listener").value(); + return (ref.toObject())["port"].toString(); +} + +bool DicomViewerProductCfg::setOurPort(QString port) +{ + if (!loadcfg()) + return false; + + QJsonValueRef ref = m_JsonRootObject.find("listener").value(); + QJsonObject obj = ref.toObject(); + obj["port"] = port; + ref = obj; + return savecfg(); +} + +bool DicomViewerProductCfg::pacsInfo(QList& info) +{ + if (!loadcfg()) + return false; + + QJsonValueRef ref = m_JsonRootObject.find("hosts").value(); + QJsonArray arr = ref.toArray(); + QJsonArray::const_iterator beg = arr.begin(); + QJsonArray::const_iterator end = arr.end(); + while (beg != end) + { + QJsonObject obj = beg->toObject(); + host h; + h.name = (obj.find("name").value().toString()); + h.ae = (obj.find("ae").value().toString()); + h.ip = (obj.find("ip").value().toString()); + h.port = (obj.find("port").value().toString()); + info.push_back(h); + beg++; + } + return true; +} + +bool DicomViewerProductCfg::setPacsInfo(QList& info) +{ + if (!loadcfg()) + return false; + + QJsonArray arr; + + QList::const_iterator beg = info.begin(); + QList::const_iterator end = info.end(); + while (beg != end) + { + QJsonObject o; + o["name"] = beg->name; + o["ae"] = beg->ae; + o["ip"] = beg->ip; + o["port"] = beg->port; + arr.append(o); + beg++; + } + + m_JsonRootObject["hosts"] = arr; + return savecfg(); +} + + +//------------------------------------------------------------------------------------------------------- +QString DicomViewerHelper::applicationPath() +{ + return QCoreApplication::applicationDirPath(); +} + +QString DicomViewerHelper::ourTitle() +{ + return DicomViewerProductCfg::GetInstance().ourTitle(); +} + +bool DicomViewerHelper::setOurTitle(QString ae) +{ + return DicomViewerProductCfg::GetInstance().setOurTitle(ae); +} + +QString DicomViewerHelper::ourPort() +{ + return DicomViewerProductCfg::GetInstance().ourPort(); +} + +bool DicomViewerHelper::setOurPort(QString port) +{ + return DicomViewerProductCfg::GetInstance().setOurPort(port); +} + +bool DicomViewerHelper::pacsInfo(QList& info) +{ + return DicomViewerProductCfg::GetInstance().pacsInfo(info); +} + +bool DicomViewerHelper::setPacsInfo(QList& info) +{ + return DicomViewerProductCfg::GetInstance().setPacsInfo(info); +} + + diff --git a/src/src/base/infinitiViewer.cxx b/src/src/base/infinitiViewer.cxx new file mode 100644 index 0000000..48e7fb7 --- /dev/null +++ b/src/src/base/infinitiViewer.cxx @@ -0,0 +1,1342 @@ +#include "infinitiViewer.h" +#include "vtkCamera.h" +#include "vtkCommand.h" +#include "vtkImageData.h" +#include "vtkInformation.h" +#include "ActorDraggableInteractorStyle.h" +#include "vtkObjectFactory.h" +#include "vtkRenderWindow.h" +#include "vtkRenderWindowInteractor.h" +#include "vtkRenderer.h" +#include "vtkStreamingDemandDrivenPipeline.h" +#include +#include +#include "vtkImageSliceMapper.h" +#include "vtkLookupTable.h" +#include "vtkScalarBarActor.h" +#include +#include "vtkPiecewiseFunction.h" +#include "vector" +#include "vtkCornerAnnotation.h" +#include "Measure.h" +#include "MeasureStore.h" +#include "vtkTextActor.h" +#include "vtkTextProperty.h" +#include "vtkLookupTable.h" +#include "RulerLegendActor.h" +#include "ColorMapReader.h" + + +#include "DicomLoader.h" + +vtkStandardNewMacro(infinitiViewer); + +bool simpleFusionableCheck(vtkImageData* a, vtkImageData* b) +{ + double* origin1 = a->GetOrigin(); + double* origin2 = b->GetOrigin(); + return (origin1[0] == origin2[0]) && (origin1[1] == origin2[1]) && (origin1[2] == origin2[2]); +} + +void infinitiViewer::SetFusionInputData(vtkImageData* data) +{ + if (!this->GetInput()) return; + if (!simpleFusionableCheck(data, this->GetInput())) return; + if (!this->FusionMapper) { + this->FusionMapper = vtkImageSliceMapper::New(); + this->FusionMapper->SliceAtFocalPointOn(); + FusionMapper->SetInputData(data); + this->FusionActor = vtkImageSlice::New(); + FusionActor->SetPickable(false); + FusionActor->SetMapper(FusionMapper); + } + else if (data == FusionMapper->GetInput()) return; + const auto prop = FusionActor->GetProperty(); + if (firstFusion) { + vtkNew table; + table->SetHueRange(1.0, 0.0); + table->SetSaturationRange(0.5, 1.0); + table->SetValueRange(0.0, 1.0); + table->SetAboveRangeColor(0.0, 0.0, 0.0, 0.0); + table->SetBelowRangeColor(0.0, 0.0, 0.0, 0.0); + table->SetNumberOfColors(512); + table->UseAboveRangeColorOn(); + table->UseBelowRangeColorOn(); + table->SetAlpha(FusionOpacity); + table->Build(); + prop->SetLookupTable(table); + firstFusion = false; + } +#ifdef IN_TEST_MODE + //vtkTextProperty* vtkTextProperty = vtkTextProperty::New(); + vtkNew LabelTextProperty; + LabelTextProperty->SetFontSize(16); + LabelTextProperty->SetBold(1); + LabelTextProperty->SetItalic(1); + LabelTextProperty->SetShadow(1); + LabelTextProperty->SetFontFamilyToArial(); + + + bar = vtkScalarBarActor::New(); + bar->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport(); + bar->SetLookupTable(prop->GetLookupTable()); + bar->SetWidth(0.1); + bar->SetHeight(0.4); + bar->SetPosition(0.04, 0.4); + //bar->DrawFrameOn(); + bar->SetMaximumHeightInPixels(250); + bar->SetMaximumWidthInPixels(50); + + bar->SetNumberOfLabels(2); + bar->SetLabelFormat("%.0f"); + + bar->UnconstrainedFontSizeOff(); + bar->SetUnconstrainedFontSize(16); + bar->SetLabelTextProperty(LabelTextProperty); + this->Renderer->AddActor(bar); + + + OpacityActor = vtkTextActor::New(); + //OpacityActor->SetInput("50%"); + OpacityActor->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport(); + OpacityActor->SetPosition(0.04, 0.35); + OpacityActor->SetTextProperty(LabelTextProperty); + SetScalarBarTitle(FusionOpacity); + + + + + this->Renderer->AddActor(OpacityActor); +#endif + +} + +void infinitiViewer::SetFusionColorLeveL(double level) +{ + if (FusionActor) + { + FusionActor->GetProperty()->SetColorLevel(level); + PrepareFusionColorTable(FusionActor->GetProperty()->GetLookupTable()); + } +} + +void infinitiViewer::SetFusionColorWindow(double window) +{ + if (FusionActor) + { + FusionActor->GetProperty()->SetColorWindow(window); + PrepareFusionColorTable(FusionActor->GetProperty()->GetLookupTable()); + } +} +void infinitiViewer::SetScalarBarTitle(double FusionOpa) +{ + int opa = int(FusionOpa * 100); + std::string str_opa = std::to_string(opa); + str_opa.append("%"); + OpacityActor->SetInput(str_opa.c_str()); +} + +void infinitiViewer::IncreFusionOpacity(double percent) +{ + if (FusionActor) { + FusionOpacity += percent; + FusionOpacity = FusionOpacity > 1.0 ? 1.0 : FusionOpacity; + FusionOpacity = FusionOpacity < 0.0 ? 0.0 : FusionOpacity; + SetScalarBarTitle(FusionOpacity); + FusionActor->GetProperty()->GetLookupTable()->SetAlpha(FusionOpacity); + PrepareFusionColorTable(FusionActor->GetProperty()->GetLookupTable()); + } +} + +void infinitiViewer::SetFusionOpacity(double opacity) +{ + if (FusionActor) { + opacity = opacity > 1.0 ? 1.0 : opacity; + opacity = opacity < 0.0 ? 0.0 : opacity; + FusionOpacity = opacity; + SetScalarBarTitle(FusionOpacity); + FusionActor->GetProperty()->GetLookupTable()->SetAlpha(FusionOpacity); + PrepareFusionColorTable(FusionActor->GetProperty()->GetLookupTable()); + } +} + +void prepareLookTable(vtkLookupTable* table) +{ + table->SetAboveRangeColor(0.0, 0.0, 0.0, 0.0); + table->SetBelowRangeColor(0.0, 0.0, 0.0, 0.0); + table->UseAboveRangeColorOn(); + table->UseBelowRangeColorOn(); +} + +void prepareTransferFunction(vtkDiscretizableColorTransferFunction* table, vtkImageProperty* prop, const std::vector>& vector) +{ + + double min = prop->GetColorLevel() - prop->GetColorWindow() * 0.5; + double max = prop->GetColorLevel() + prop->GetColorWindow() * 0.5; + int size = table->GetSize(); + + table->RemoveAllPoints(); + for (int i = 0; i < size; i++) + { + table->AddRGBPoint(min + vector[i][0] * prop->GetColorWindow(), + vector[i][1], vector[i][2], vector[i][3], vector[i][4], vector[i][5]); + } + vtkNew< vtkPiecewiseFunction> wise; + wise->AddPoint(min - 0.05, 0.0); + wise->AddPoint(min, table->GetAlpha()); + wise->AddPoint(max, table->GetAlpha()); + //wise->AddPoint(max + 0.05, 0.0); + wise->ClampingOn(); //Default is On + table->SetScalarOpacityFunction(wise); + table->EnableOpacityMappingOn(); +} + + +void infinitiViewer::SetFusionColorTable(vtkScalarsToColors* table) +{ + if (!FusionActor) return; + PrepareFusionColorTable(table, true); + FusionActor->GetProperty()->SetLookupTable(table); +#ifdef IN_TEST_MODE + bar->SetLookupTable(table); +#endif +} + +void infinitiViewer::SetFusionColorPreset(const char* preset) +{ + auto values = ColorMapReader::DefaultPresets()->GetPresetValues(preset); + auto PresetType = ColorMapReader::DefaultPresets()->GetPresetType(preset); + + switch (PresetType) { + case ColorMapReader::RGBPoints: + { + vtkNew< vtkDiscretizableColorTransferFunction> table; + for (int i = 0; i + 3 < values.size(); i += 4) { + table->AddRGBPoint(values[i], values[i + 1], values[i + 2], values[i + 3]); + } + double* nancolor = ColorMapReader::DefaultPresets()->GetPresetNanColor(preset); + if (nancolor) + { + table->SetNanColor(nancolor); + } + this->SetFusionColorTable(table); + break; + } + case ColorMapReader::IndexedColors: { + vtkNew< vtkDiscretizableColorTransferFunction> table; + for (int i = 0; i + 2 < values.size() - 1; i += 2) { + table->AddRGBPoint(((double)i) / ((double)(values.size() - 3)), values[i], values[i + 1], + values[i + 2], 1.0, 1.0); + table->AddRGBPoint(1.0, values[values.size() - 3], values[values.size() - 2], + values[values.size() - 1]); + double* nancolor = ColorMapReader::DefaultPresets()->GetPresetNanColor(preset); + if (nancolor) { + table->SetNanColor(nancolor); + } + this->SetFusionColorTable(table); + break; + } + } + default: + break; + } +} + +void infinitiViewer::PrepareFusionColorTable(vtkScalarsToColors* table, bool reset) +{ + const auto prop = FusionActor->GetProperty(); + table->SetAlpha(FusionOpacity); + if (table->IsA("vtkLookupTable")) { + auto t = vtkLookupTable::SafeDownCast(table); + if (!t) return; + prepareLookTable(t); + } + else if (table->IsA("vtkDiscretizableColorTransferFunction")) + { + auto t = vtkDiscretizableColorTransferFunction::SafeDownCast(table); + if (!t) return; + if (reset) + { + int size = t->GetSize(); + fusion_tf_vector.clear(); + for (int i = 0; i < size; i++) + { + double v[6] = { 0,0,0,0,0,0 }; + t->GetNodeValue(i, v); + std::vector nt = { v[0],v[1],v[2],v[3],v[4],v[5] }; + fusion_tf_vector.push_back(nt); + } + } + prepareTransferFunction(t, prop, fusion_tf_vector); + } + table->Build(); +} + +void infinitiViewer::RemoveFusionActor() +{ + this->Renderer->RemoveActor(bar); + bar->Delete(); + bar = nullptr; + this->Renderer->RemoveActor(OpacityActor); + OpacityActor->Delete(); + OpacityActor = nullptr; +} +void infinitiViewer::RemoveFusionData() +{ + if (!FusionMapper) return; + if (Renderer->HasViewProp(FusionActor)) Renderer->RemoveViewProp(FusionActor); + FusionMapper->Delete(); + FusionMapper = nullptr; + FusionActor->Delete(); + FusionActor = nullptr; + //reset first fusion flag + firstFusion = true; + + RemoveFusionActor(); + //this->Render(); +} + +//---------------------------------------------------------------------------- +infinitiViewer::infinitiViewer() +{ + this->RenderWindow = nullptr; + this->Renderer = nullptr; + this->ImageActor = vtkImageSlice::New(); + this->ImageMapper = vtkImageSliceMapper::New(); + this->ImageMapper->SliceAtFocalPointOn(); + this->ImageActor->SetMapper(this->ImageMapper); + this->FusionActor = nullptr; + this->FusionMapper = nullptr; + this->Interactor = nullptr; + this->InteractorStyle = nullptr; + + + this->cornerAnnotation = vtkCornerAnnotation::New(); + vtkNew prop; + prop->SetFontFamilyToArial(); + prop->ShadowOn(); + this->cornerAnnotation->SetTextProperty(prop); + this->cornerAnnotation->GetTextProperty()->SetColor(1, 1, 0.5); + // Annotate the image with window/level and mouse over pixel information + cornerAnnotation->SetLinearFontScaleFactor(2); + cornerAnnotation->SetNonlinearFontScaleFactor(1); + cornerAnnotation->SetMaximumFontSize(FontSizeHelper::font_size); + + + + this->Slice = 0; + this->FirstRender = 1; + this->SliceOrientation = infinitiViewer::SLICE_ORIENTATION_XY; + + // measureStore = new MeasureStore; + measureStore = MeasureStore::Instance(); + // Setup the pipeline + + vtkRenderWindow* renwin = vtkRenderWindow::New(); + this->SetRenderWindow(renwin); + renwin->Delete(); + + + vtkRenderer* ren = vtkRenderer::New(); + this->SetRenderer(ren); + ren->Delete(); + + this->InstallPipeline(); + + this->AddObserver(infinitiViewerEvents::SlicedEvent, this, &infinitiViewer::LoadMeasures); + this->AddObserver(infinitiViewerEvents::SlicedEvent, this, &infinitiViewer::updateTopLeftCornerInfo); + + uintptr_t handler = reinterpret_cast(this); + sprintf(SOP_UID, "%llu", handler); +} + +//---------------------------------------------------------------------------- +infinitiViewer::~infinitiViewer() +{ + if (this->ImageMapper) + { + this->ImageMapper->Delete(); + this->ImageMapper = nullptr; + } + if (this->ImageActor) + { + this->ImageActor->Delete(); + this->ImageActor = nullptr; + } + if (this->FusionMapper) + { + this->FusionMapper->Delete(); + this->FusionMapper = nullptr; + } + if (this->FusionActor) + { + this->FusionActor->Delete(); + this->FusionActor = nullptr; + } + + if (this->Renderer) + { + this->Renderer->Delete(); + this->Renderer = nullptr; + } + + if (this->RenderWindow) + { + this->RenderWindow->Delete(); + this->RenderWindow = nullptr; + } + + if (this->Interactor) + { + this->Interactor->Delete(); + this->Interactor = nullptr; + } + + if (this->InteractorStyle) + { + this->InteractorStyle->Delete(); + this->InteractorStyle = nullptr; + } + measureStore->RemoveAllInSeries(SOP_UID); + // measureStore->Clear(); + //delete measureStore; +} + +//---------------------------------------------------------------------------- +void infinitiViewer::SetupInteractor(vtkRenderWindowInteractor* arg) +{ + if (this->Interactor == arg) + { + return; + } + + this->UnInstallPipeline(); + + if (this->Interactor) + { + this->Interactor->UnRegister(this); + } + + this->Interactor = arg; + + if (this->Interactor) + { + this->Interactor->Register(this); + } + + this->InstallPipeline(); + + if (this->Renderer) + { + this->Renderer->GetActiveCamera()->ParallelProjectionOn(); + } +} + +//---------------------------------------------------------------------------- +void infinitiViewer::SetRenderWindow(vtkRenderWindow* arg) +{ + if (this->RenderWindow == arg) + { + return; + } + + this->UnInstallPipeline(); + + if (this->RenderWindow) + { + this->RenderWindow->UnRegister(this); + } + + this->RenderWindow = arg; + + if (this->RenderWindow) + { + this->RenderWindow->Register(this); + } + this->RenderWindow->AddObserver(vtkCommand::EventIds::RenderEvent, this, &infinitiViewer::RenderRuler); + this->InstallPipeline(); +} + +//---------------------------------------------------------------------------- +void infinitiViewer::SetRenderer(vtkRenderer* arg) +{ + if (this->Renderer == arg) + { + return; + } + + this->UnInstallPipeline(); + + if (this->Renderer) + { + this->Renderer->UnRegister(this); + } + + this->Renderer = arg; + + if (this->Renderer) + { + this->Renderer->Register(this); + } + + this->InstallPipeline(); + this->UpdateOrientation(); +} + +//---------------------------------------------------------------------------- +void infinitiViewer::SetSize(int width, int height) +{ + this->RenderWindow->SetSize(width, height); +} + +//---------------------------------------------------------------------------- +int* infinitiViewer::GetSize() +{ + return this->RenderWindow->GetSize(); +} + +//---------------------------------------------------------------------------- +void infinitiViewer::GetSliceRange(int& min, int& max) +{ + vtkAlgorithm* input = this->GetInputAlgorithm(); + if (input) + { + input->UpdateInformation(); + int* w_ext = + input->GetOutputInformation(0)->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()); + min = w_ext[this->SliceOrientation * 2]; + max = w_ext[this->SliceOrientation * 2 + 1]; + } +} + +//---------------------------------------------------------------------------- +int* infinitiViewer::GetSliceRange() +{ + vtkAlgorithm* input = this->GetInputAlgorithm(); + if (input) + { + input->UpdateInformation(); + return input->GetOutputInformation(0)->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()) + + this->SliceOrientation * 2; + } + return nullptr; +} + +//---------------------------------------------------------------------------- +int infinitiViewer::GetSliceMin() +{ + int* range = this->GetSliceRange(); + if (range) + { + return range[0]; + } + return 0; +} + +//---------------------------------------------------------------------------- +int infinitiViewer::GetSliceMax() +{ + int* range = this->GetSliceRange(); + if (range) + { + return range[1]; + } + return 0; +} + +//---------------------------------------------------------------------------- +void infinitiViewer::SetSlice(int slice) +{ + int* range = this->GetSliceRange(); + if (range) + { + if (slice < range[0]) + { + slice = range[0]; + } + else if (slice > range[1]) + { + slice = range[1]; + } + } + + int lastSliceNumber = this->ImageMapper->GetSliceNumber(); + if (lastSliceNumber == slice) + { + return; + } + + this->Slice = slice; + this->Modified(); + //this->ImageMapper->SetSliceNumber(slice); + vtkCamera* camera = this->Renderer->GetActiveCamera(); + vtkAlgorithm* input = this->GetInputAlgorithm(); + if (!input || !this->ImageActor) + { + return; + } + + input->UpdateInformation(); + vtkInformation* outInfo = input->GetOutputInformation(0); + double* spacing = outInfo->Get(vtkDataObject::SPACING()); + double* origin = outInfo->Get(vtkDataObject::ORIGIN()); + double* pos = camera->GetPosition(); + + double npv = origin[this->SliceOrientation] + this->Slice * spacing[this->SliceOrientation]; + camera->SetDistance(fabs(npv - pos[this->SliceOrientation])); + this->Render(); + this->InvokeEvent(infinitiViewerEvents::SlicedEvent, nullptr); +} + +int infinitiViewer::GetSlice() { + return this->ImageMapper->GetSliceNumber(); +} + +void infinitiViewer::SetZoomScale(double scale) { + if (Renderer) + { + Renderer->GetActiveCamera()->SetParallelScale(scale); + } +} + +void infinitiViewer::SetPanOffset(double* p) { + double fp[3] = { 0.0,0.0,0.0 }; + Renderer->GetActiveCamera()->GetPosition(fp); + fp[0] = fp[0] + p[0]; + fp[1] = fp[1] + p[1]; + fp[2] = fp[2] + p[2]; + Renderer->GetActiveCamera()->SetPosition(fp); + Renderer->GetActiveCamera()->GetFocalPoint(fp); + fp[0] = fp[0] + p[0]; + fp[1] = fp[1] + p[1]; + fp[2] = fp[2] + p[2]; + Renderer->GetActiveCamera()->SetFocalPoint(fp); + +} + + + +//---------------------------------------------------------------------------- +void infinitiViewer::SetSliceOrientation(int orientation) +{ + if (orientation < infinitiViewer::SLICE_ORIENTATION_YZ || + orientation > infinitiViewer::SLICE_ORIENTATION_XY) + { + vtkErrorMacro("Error - invalid slice orientation " << orientation); + return; + } + + if (this->SliceOrientation == orientation) + { + return; + } + + this->SliceOrientation = orientation; + + // Update the viewer + + int* range = this->GetSliceRange(); + if (range) + { + this->Slice = static_cast((range[0] + range[1]) * 0.5); + } + + this->UpdateOrientation(); + // this->UpdateDisplayExtent(); + + if (this->Renderer && this->GetInput()) + { + double scale = this->Renderer->GetActiveCamera()->GetParallelScale(); + this->Renderer->ResetCamera(); + this->Renderer->GetActiveCamera()->SetParallelScale(scale); + } + + this->Render(); +} + +//---------------------------------------------------------------------------- +void infinitiViewer::UpdateOrientation() +{ + // Set the camera position + + vtkCamera* cam = this->Renderer ? this->Renderer->GetActiveCamera() : nullptr; + if (cam) + { + switch (this->SliceOrientation) + { + case infinitiViewer::SLICE_ORIENTATION_XY: + cam->SetFocalPoint(0, 0, 0); + cam->SetPosition(0, 0, 1); // -1 if medical ? + cam->SetViewUp(0, 1, 0); + break; + + case infinitiViewer::SLICE_ORIENTATION_XZ: + cam->SetFocalPoint(0, 0, 0); + cam->SetPosition(0, -1, 0); // 1 if medical ? + cam->SetViewUp(0, 0, 1); + break; + + case infinitiViewer::SLICE_ORIENTATION_YZ: + cam->SetFocalPoint(0, 0, 0); + cam->SetPosition(1, 0, 0); // -1 if medical ? + cam->SetViewUp(0, 0, 1); + break; + } + } +} + +//---------------------------------------------------------------------------- +void infinitiViewer::SetPosition(int x, int y) +{ + this->RenderWindow->SetPosition(x, y); +} + +//---------------------------------------------------------------------------- +int* infinitiViewer::GetPosition() +{ + return this->RenderWindow->GetPosition(); +} + +//---------------------------------------------------------------------------- +void infinitiViewer::SetDisplayId(void* a) +{ + this->RenderWindow->SetDisplayId(a); +} + +//---------------------------------------------------------------------------- +void infinitiViewer::SetWindowId(void* a) +{ + this->RenderWindow->SetWindowId(a); +} + +//---------------------------------------------------------------------------- +void infinitiViewer::SetParentId(void* a) +{ + this->RenderWindow->SetParentId(a); +} + +//---------------------------------------------------------------------------- +double infinitiViewer::GetColorWindow() +{ + return this->ImageActor->GetProperty()->GetColorWindow(); +} + +//---------------------------------------------------------------------------- +double infinitiViewer::GetColorLevel() +{ + return this->ImageActor->GetProperty()->GetColorLevel(); +} + +//---------------------------------------------------------------------------- +void infinitiViewer::SetColorWindow(double s) +{ + this->ImageActor->GetProperty()->SetColorWindow(s); +} + +//---------------------------------------------------------------------------- +void infinitiViewer::SetColorLevel(double s) +{ + this->ImageActor->GetProperty()->SetColorLevel(s); +} + +void infinitiViewer::SetNegativeMode(bool negative) +{ + if (negative) { + double window = this->ImageActor->GetProperty()->GetColorWindow(); + double level = this->ImageActor->GetProperty()->GetColorLevel(); + + vtkNew lut; + lut->SetRange(level - 0.5 * window, level + 0.5 * window); // image intensity range + lut->SetValueRange(1.0, 0.0); // from black to white + lut->SetHueRange(0.0, 0.0); + lut->SetSaturationRange(0.0, 0.0); + //lookupTable->SetRampToLinear(); + lut->Build(); + this->SetLookupTable(lut); + this->Render(); + } + else + { + this->SetLookupTable(nullptr); + this->Render(); + } +} + +void infinitiViewer::SetLookupTable(vtkLookupTable* lut) +{ + this->ImageActor->GetProperty()->SetLookupTable(lut); +} + + +//---------------------------------------------------------------------------- +void infinitiViewer::InstallPipeline() +{ + if (this->RenderWindow && this->Renderer) + { + this->RenderWindow->AddRenderer(this->Renderer); + } + + if (this->Interactor) + { + if (!this->InteractorStyle) + { + this->InteractorStyle = ActorDraggableInteractorStyle::New(); + this->InteractorStyle->SetInteractionModeToImageSlicing(); + this->InteractorStyle->SetCurrentImageNumber(0); + this->InteractorStyle->SetCornerAnnotation(cornerAnnotation); + + this->InteractorStyle->AddObserver(ActorDraggableInteractorStyle::DraggableStyleEvents::EndMeasureEvent, this, &infinitiViewer::AddMeasures); + this->InteractorStyle->AddObserver(ActorDraggableInteractorStyle::DraggableStyleEvents::DeleteMeasureEvent, this, &infinitiViewer::RemoveMeasures); + this->InteractorStyle->AddObserver(ActorDraggableInteractorStyle::DraggableStyleEvents::SlicedEvent, this, &infinitiViewer::LoadMeasures); + + //for convert vtkEvent to Qt signal + this->InteractorStyle->AddObserver(ActorDraggableInteractorStyle::DraggableStyleEvents::SlicedEvent, this, &infinitiViewer::raiseEvent); + this->InteractorStyle->AddObserver(ActorDraggableInteractorStyle::DraggableStyleEvents::EndDollyEvent, this, &infinitiViewer::raiseEvent); + this->InteractorStyle->AddObserver(vtkCommand::EventIds::EndWindowLevelEvent, this, &infinitiViewer::raiseEvent); + } + + this->Interactor->SetInteractorStyle(this->InteractorStyle); + this->Interactor->SetRenderWindow(this->RenderWindow); + } + + if (this->Renderer && this->ImageActor) + { + this->Renderer->AddViewProp(this->ImageActor); + this->Renderer->GetActiveCamera()->SetParallelProjection(1); + this->Renderer->SetBackground(0.0, 0.0, 0.0); + } + if (this->Renderer && this->FusionActor && Fusion) + { + this->Renderer->AddViewProp(this->FusionActor); + } + if (this->Renderer && this->cornerAnnotation) + { + this->Renderer->AddViewProp(cornerAnnotation); + } + loadedMeasureSlice = -1; +} + +//---------------------------------------------------------------------------- +void infinitiViewer::UnInstallPipeline() +{ + if (this->ImageActor && this->ImageMapper) + { + // this->ImageActor->SetMapper(nullptr); + } + if (this->Renderer && this->FusionActor && this->Renderer->HasViewProp(FusionActor)) + { + this->Renderer->RemoveViewProp(this->FusionActor); + } + if (this->Renderer && this->ImageActor) + { + this->Renderer->RemoveViewProp(this->ImageActor); + } + + if (this->RenderWindow && this->Renderer) + { + this->RenderWindow->RemoveRenderer(this->Renderer); + } + + if (this->Interactor) + { + this->Interactor->SetInteractorStyle(nullptr); + this->Interactor->SetRenderWindow(nullptr); + } +} + +void infinitiViewer::LoadMeasures(bool forceReload) { + if (!forceReload && this->loadedMeasureSlice == this->ImageMapper->GetSliceNumber()) return; + + this->loadedMeasureSlice = this->ImageMapper->GetSliceNumber(); + ClearCurrentSliceMeasure(); + ReloadCurrentSliceMeasure(); +} + +void infinitiViewer::ReloadCurrentSliceMeasure() { + list = measureStore->GetMeasures(SOP_UID, ImageMapper->GetSliceNumber()); + if (list) + { + for (int i = 0; i < list->length(); i++) { + auto item = list->value(i); + auto d = dynamic_cast(item); + if (d) d->SetRenderer(GetRenderer()); + } + } +} + +void infinitiViewer::ClearCurrentSliceMeasure() const { + if (list) + { + for (int i = 0; i < list->length(); i++) { + auto item = list->value(i); + auto d = dynamic_cast(item); + if (d) d->SetRenderer(nullptr); + } + } +} + +void infinitiViewer::AddMeasures(vtkObject*, unsigned long eventid, void* calldata) { + auto m = static_cast(calldata); + if (m->Valid()) { + measureStore->Store(SOP_UID, this->ImageMapper->GetSliceNumber(), m); + } + LoadMeasures(true); + this->Render(); +} + +void infinitiViewer::RemoveMeasures(vtkObject*, unsigned long eventid, void* calldata) { + auto p = static_cast(calldata); + auto da = DraggableActor::SafeDownCast(p); + auto m = dynamic_cast(da); + ClearCurrentSliceMeasure(); + measureStore->Remove(m); + this->InteractorStyle->ClearSelectedProp(); + ReloadCurrentSliceMeasure(); + this->Render(); +} + +void infinitiViewer::DeleteSelectedMeasure() { + if (this->InteractorStyle->GetSelectedProp()) + { + RemoveMeasures(nullptr, 0, this->InteractorStyle->GetSelectedProp()); + } +} + +void infinitiViewer::DeleteCurrentSliceMeasure() { + this->InteractorStyle->ClearSelectedProp(); + ClearCurrentSliceMeasure(); + measureStore->RemoveAllInSlice(SOP_UID, this->ImageMapper->GetSliceNumber()); + ReloadCurrentSliceMeasure(); + this->Render(); +} + +void infinitiViewer::DeleteCurrentSeriesMeasure() { + this->InteractorStyle->ClearSelectedProp(); + ClearCurrentSliceMeasure(); + measureStore->RemoveAllInSeries(SOP_UID); + ReloadCurrentSliceMeasure(); + this->Render(); +} + +//---------------------------------------------------------------------------- +void infinitiViewer::Render() +{ + if (this->FirstRender) + { + // // Initialize the size if not set yet + + vtkAlgorithm* input = this->GetInputAlgorithm(); + if (input) + { + input->UpdateInformation(); + int* w_ext = + this->GetInputInformation()->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()); + int xs = 0, ys = 0; + + switch (this->SliceOrientation) + { + case infinitiViewer::SLICE_ORIENTATION_XY: + default: + xs = w_ext[1] - w_ext[0] + 1; + ys = w_ext[3] - w_ext[2] + 1; + break; + + case infinitiViewer::SLICE_ORIENTATION_XZ: + xs = w_ext[1] - w_ext[0] + 1; + ys = w_ext[5] - w_ext[4] + 1; + break; + + case infinitiViewer::SLICE_ORIENTATION_YZ: + xs = w_ext[3] - w_ext[2] + 1; + ys = w_ext[5] - w_ext[4] + 1; + break; + } + + // if it would be smaller than 150 by 100 then limit to 150 by 100 + if (this->RenderWindow->GetSize()[0] == 0) + { + this->RenderWindow->SetSize(xs < 150 ? 150 : xs, ys < 100 ? 100 : ys); + } + + if (this->Renderer) + { + this->Renderer->ResetCamera(); + auto camera = this->Renderer->GetActiveCamera(); + //camera 蹇呴』瑕佸姩涓涓嬶紝鐩存帴loadfusion鎵嶈兘姝e父鏄剧ず + int lastIndex = ImageMapper->GetSliceNumber(); + //camera->GetPosition(cameraPosition); + double distance = camera->GetDistance(); + camera->SetDistance(distance - 0.005); + //camera->GetPosition(cameraPosition); + int currentIndex = ImageMapper->GetSliceNumber(); + if (currentIndex != lastIndex) this->SetSlice(lastIndex); + this->Renderer->GetActiveCamera()->SetParallelScale(xs < 150 ? 75 : (xs - 1) / 2.0); + + //calibration of image by VFlip + //this->Renderer->GetActiveCamera()->Elevation(-180); + //this->Renderer->GetActiveCamera()->Roll(180); + //this->Renderer->ResetCameraClippingRange(); + + } + this->FirstRender = 0; + } + } + if (this->GetInput()) + { + if (Fusion && FusionActor && !this->Renderer->HasViewProp(FusionActor)) { + this->Renderer->AddActor(FusionActor); + } + if (!Fusion && FusionActor && this->Renderer->HasViewProp(FusionActor)) + { + this->Renderer->RemoveViewProp(FusionActor); + } + this->LoadMeasures(); + this->RenderWindow->Render(); + } +} + +//---------------------------------------------------------------------------- +const char* infinitiViewer::GetWindowName() +{ + return this->RenderWindow->GetWindowName(); +} + +//---------------------------------------------------------------------------- +void infinitiViewer::SetOffScreenRendering(vtkTypeBool i) +{ + this->RenderWindow->SetOffScreenRendering(i); +} + +//---------------------------------------------------------------------------- +vtkTypeBool infinitiViewer::GetOffScreenRendering() +{ + return this->RenderWindow->GetOffScreenRendering(); +} + +//---------------------------------------------------------------------------- +void infinitiViewer::SetInputData(vtkImageData* in) +{ +#ifdef _DEBUG + printf("fusion origin:%f,%f,%f", in->GetOrigin()[0], in->GetOrigin()[1], in->GetOrigin()[2]); + printf("fusion spacing:%f,%f,%f", in->GetSpacing()[0], in->GetSpacing()[1], in->GetSpacing()[2]); +#endif + this->ImageActor->GetMapper()->SetInputData(in); + this->RemoveFusionData(); + // this->UpdateDisplayExtent(); +} + +//---------------------------------------------------------------------------- +vtkImageData* infinitiViewer::GetInput() +{ + return this->ImageMapper->GetInput(); +} + +//---------------------------------------------------------------------------- +vtkInformation* infinitiViewer::GetInputInformation() +{ + return this->ImageMapper->GetInputInformation(); +} + +//---------------------------------------------------------------------------- +vtkAlgorithm* infinitiViewer::GetInputAlgorithm() +{ + return this->ImageMapper->GetInputAlgorithm(); +} + +//---------------------------------------------------------------------------- +void infinitiViewer::SetInputConnection(vtkAlgorithmOutput* input) +{ + this->ImageMapper->SetInputConnection(input); + this->RemoveFusionData(); +} + +void infinitiViewer::ActiveMeasure(Measure* m) { + this->InteractorStyle->ActiveMeasure(m); +} + + +void infinitiViewer::UnActiveMeasure() { + this->InteractorStyle->UnActiveMeasure(); +} + +//---------------------------------------------------------------------------- +void infinitiViewer::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); + + os << indent << "RenderWindow:\n"; + this->RenderWindow->PrintSelf(os, indent.GetNextIndent()); + os << indent << "Renderer:\n"; + this->Renderer->PrintSelf(os, indent.GetNextIndent()); + os << indent << "ImageActor:\n"; + this->ImageActor->PrintSelf(os, indent.GetNextIndent()); + os << indent << "ImageMapper:\n" << endl; + this->ImageMapper->PrintSelf(os, indent.GetNextIndent()); + os << indent << "Slice: " << this->Slice << endl; + os << indent << "SliceOrientation: " << this->SliceOrientation << endl; + os << indent << "InteractorStyle: " << endl; + if (this->InteractorStyle) + { + os << "\n"; + this->InteractorStyle->PrintSelf(os, indent.GetNextIndent()); + } + else + { + os << "None"; + } +} + +void infinitiViewer::RenderRuler() { + if (AnnoHelper::IsAnno()) { + if (Renderer) + { + ruler->RenderOverlay(Renderer); + } + } +} + +void infinitiViewer::SwitchToNextPreset() +{ + if (ColorMapReader::DefaultPresets()) { + + //const char* preset3 = ColorMapReader::DefaultPresets()->GetPresetNames().at(currentPresetIndex).data(); + //const string& preset = ColorMapReader::DefaultPresets()->GetPresetNames().at(currentPresetIndex); + string preset = ColorMapReader::DefaultPresets()->GetPresetNames().at(currentPresetIndex); + printf("current preset name:%s\r\n", preset.c_str()); + currentPresetIndex++; + currentPresetIndex = currentPresetIndex > 193 ? 0 : currentPresetIndex; + SetFusionColorPreset(preset.c_str()); + this->Render(); + } +} + +// helper class to format slice status message +class StatusMessage +{ +public: + static std::string Format(int slice, int maxSlice) + { + std::stringstream tmp; + tmp << "Slice Number " << slice + 1 << "/" << maxSlice + 1; + return tmp.str(); + } + static std::string Format(std::string msg1, std::string msg2) + { + std::stringstream tmp; + tmp << msg1 << "\n" << msg2; + return tmp.str(); + } + +}; +void infinitiViewer::updateTopLeftCornerInfo() +{ + if (AnnoHelper::IsAnno()) { + int _MaxSlice = this->GetSliceMax(); + int _Slice = this->GetSlice(); + std::string msg_const = m_cornerInfo.ConstAnno[TOP_LEFT]; + std::string msg_var = StatusMessage::Format(_Slice, _MaxSlice); + std::string msg_upleft = StatusMessage::Format(msg_var, msg_const); + cornerAnnotation->SetText(TOP_LEFT, msg_upleft.c_str()); + } + this->Render(); +} + +void infinitiViewer::updateCornerInfo(int index) +{ + if (AnnoHelper::IsAnno()) { + + if (index == TOP_LEFT) + { + int _MaxSlice = this->GetSliceMax(); + int _Slice = this->GetSlice(); + //cout << "MoveSliceForward::Slice = " << _Slice << std::endl; + std::string msg_const = m_cornerInfo.ConstAnno[index]; + std::string msg_var = StatusMessage::Format(_Slice, _MaxSlice); + std::string msg_upleft = StatusMessage::Format(msg_var, msg_const); + //_StatusMapper->SetInput(msg.c_str()); + cornerAnnotation->SetText(index, msg_upleft.c_str()); + + } + + else if (index == BOTTOM_RIGHT) + { + std::string message = "WL/WW:"; + message += vtkVariant((int)this->GetColorLevel()).ToString(); + message += " / "; + message += vtkVariant((int)this->GetColorWindow()).ToString(); + + + cornerAnnotation->SetText(index, message.c_str()); + //m_renderWindowInteractor->Render(); + + //m_imageViewer->Modified(); + } + else if (index == TOP_RIGHT) + { + if (AnnoHelper::IsPrivacy()) + { + cornerAnnotation->SetText(index, m_cornerInfo.ConstAnno[TOP_RIGHT_PRIVACY].c_str()); + } + else + { + cornerAnnotation->SetText(index, m_cornerInfo.ConstAnno[index].c_str()); + } + + } + else + { + cornerAnnotation->SetText(index, m_cornerInfo.ConstAnno[index].c_str()); + } + } + this->Render(); +} + + +void infinitiViewer::initTopLeftCornerInfo(const std::string& lbl_ser_num, const std::string& ser_num) +{ + m_cornerInfo.ConstAnno[TOP_LEFT].append(lbl_ser_num); + m_cornerInfo.ConstAnno[TOP_LEFT].append(" "); + m_cornerInfo.ConstAnno[TOP_LEFT].append(ser_num); +} + +void infinitiViewer::initCornerInfo(DicomTagInfo_t* pSeriesTags) +{ + + m_cornerInfo.win_level = pSeriesTags->WL; + //m_cornerInfo.ConstAnno[BOTTOM_RIGHT].append("WL "); + //m_cornerInfo.ConstAnno[BOTTOM_RIGHT].append(std::to_string(pSeriesTags->WL)); + //m_cornerInfo.ConstAnno[BOTTOM_RIGHT].append("\n"); + + + m_cornerInfo.win_width = pSeriesTags->WW; + //m_cornerInfo.ConstAnno[BOTTOM_RIGHT].append("WW "); + //m_cornerInfo.ConstAnno[BOTTOM_RIGHT].append(std::to_string(pSeriesTags->WW)); + + + m_cornerInfo.ConstAnno[TOP_LEFT].append(pSeriesTags->lbl_ser_num); + m_cornerInfo.ConstAnno[TOP_LEFT].append(" "); + m_cornerInfo.ConstAnno[TOP_LEFT].append(pSeriesTags->m_SeriesNumber); + + m_cornerInfo.ConstAnno[TOP_RIGHT].append(pSeriesTags->m_PatientName); + m_cornerInfo.ConstAnno[TOP_RIGHT].append("\n"); + m_cornerInfo.ConstAnno[TOP_RIGHT].append(pSeriesTags->m_StudyDescription); + m_cornerInfo.ConstAnno[TOP_RIGHT].append("\n"); + m_cornerInfo.ConstAnno[TOP_RIGHT].append(pSeriesTags->m_SeriesDescription); + m_cornerInfo.ConstAnno[TOP_RIGHT].append("\n"); + m_cornerInfo.ConstAnno[TOP_RIGHT].append(pSeriesTags->m_Institution); + m_cornerInfo.ConstAnno[TOP_RIGHT].append("\n"); + + + m_cornerInfo.ConstAnno[TOP_RIGHT_PRIVACY].append("****"); + m_cornerInfo.ConstAnno[TOP_RIGHT_PRIVACY].append("\n"); + m_cornerInfo.ConstAnno[TOP_RIGHT_PRIVACY].append(pSeriesTags->m_StudyDescription); + m_cornerInfo.ConstAnno[TOP_RIGHT_PRIVACY].append("\n"); + m_cornerInfo.ConstAnno[TOP_RIGHT_PRIVACY].append(pSeriesTags->m_SeriesDescription); + m_cornerInfo.ConstAnno[TOP_RIGHT_PRIVACY].append("\n"); + m_cornerInfo.ConstAnno[TOP_RIGHT_PRIVACY].append("****"); + m_cornerInfo.ConstAnno[TOP_RIGHT_PRIVACY].append("\n"); + + std::vector* orien_list = OrienHelper::getOrienStrList(pSeriesTags->m_orientation); + //currently only adapt to USCT Format + if (orien_list) + { + m_cornerInfo.ConstAnno[BOTTOM_MIDDLE].append(orien_list->at(BOTTOM_MIDDLE - 4)); + m_cornerInfo.ConstAnno[BOTTOM_MIDDLE].append("\n"); + + m_cornerInfo.ConstAnno[RIGHT_MIDDLE].append(orien_list->at(RIGHT_MIDDLE - 4)); + m_cornerInfo.ConstAnno[RIGHT_MIDDLE].append("\n"); + + + m_cornerInfo.ConstAnno[LEFT_MIDDLE].append(orien_list->at(LEFT_MIDDLE - 4)); + m_cornerInfo.ConstAnno[LEFT_MIDDLE].append("\n"); + + m_cornerInfo.ConstAnno[TOP_MIDDLE].append(orien_list->at(TOP_MIDDLE - 4)); + m_cornerInfo.ConstAnno[TOP_MIDDLE].append("\n"); + } +} + +void infinitiViewer::updateOrienInfo(TransFormType type) +{ + + char top_middle[20]; + char left_middle[20]; + + + switch (type) + { + case ROTATE_90_CCW: + strcpy_s(top_middle, cornerAnnotation->GetText(TOP_MIDDLE)); + cornerAnnotation->SetText(TOP_MIDDLE, cornerAnnotation->GetText(RIGHT_MIDDLE)); + cornerAnnotation->SetText(RIGHT_MIDDLE, cornerAnnotation->GetText(BOTTOM_MIDDLE)); + cornerAnnotation->SetText(BOTTOM_MIDDLE, cornerAnnotation->GetText(LEFT_MIDDLE)); + cornerAnnotation->SetText(LEFT_MIDDLE, top_middle); + break; + case ROTATE_90_CW: + strcpy_s(top_middle, cornerAnnotation->GetText(TOP_MIDDLE)); + cornerAnnotation->SetText(TOP_MIDDLE, cornerAnnotation->GetText(LEFT_MIDDLE)); + cornerAnnotation->SetText(LEFT_MIDDLE, cornerAnnotation->GetText(BOTTOM_MIDDLE)); + cornerAnnotation->SetText(BOTTOM_MIDDLE, cornerAnnotation->GetText(RIGHT_MIDDLE)); + cornerAnnotation->SetText(RIGHT_MIDDLE, top_middle); + break; + case ROTATE_180: + strcpy_s(top_middle, cornerAnnotation->GetText(TOP_MIDDLE)); + strcpy_s(left_middle, cornerAnnotation->GetText(LEFT_MIDDLE)); + cornerAnnotation->SetText(TOP_MIDDLE, cornerAnnotation->GetText(BOTTOM_MIDDLE)); + cornerAnnotation->SetText(LEFT_MIDDLE, cornerAnnotation->GetText(RIGHT_MIDDLE)); + cornerAnnotation->SetText(BOTTOM_MIDDLE, top_middle); + cornerAnnotation->SetText(RIGHT_MIDDLE, left_middle); + break; + case H_FLIP: + strcpy_s(left_middle, cornerAnnotation->GetText(LEFT_MIDDLE)); + cornerAnnotation->SetText(LEFT_MIDDLE, cornerAnnotation->GetText(RIGHT_MIDDLE)); + cornerAnnotation->SetText(RIGHT_MIDDLE, left_middle); + break; + case V_FLIP: + strcpy_s(top_middle, cornerAnnotation->GetText(TOP_MIDDLE)); + cornerAnnotation->SetText(TOP_MIDDLE, cornerAnnotation->GetText(BOTTOM_MIDDLE)); + cornerAnnotation->SetText(BOTTOM_MIDDLE, top_middle); + break; + case CLEAR: + cornerAnnotation->SetText(TOP_MIDDLE, m_cornerInfo.ConstAnno[TOP_MIDDLE].c_str()); + cornerAnnotation->SetText(BOTTOM_MIDDLE, m_cornerInfo.ConstAnno[BOTTOM_MIDDLE].c_str()); + cornerAnnotation->SetText(LEFT_MIDDLE, m_cornerInfo.ConstAnno[LEFT_MIDDLE].c_str()); + cornerAnnotation->SetText(RIGHT_MIDDLE, m_cornerInfo.ConstAnno[RIGHT_MIDDLE].c_str()); + break; + default: + break; + } + cornerAnnotation->Modified(); + this->Render(); +} + + +void infinitiViewer::updateCornerInfoAll() +{ + if (AnnoHelper::IsAnno()) { + updateCornerInfo(TOP_LEFT); + updateCornerInfo(BOTTOM_RIGHT); + updateCornerInfo(TOP_RIGHT); + + + updateCornerInfo(BOTTOM_MIDDLE); + updateCornerInfo(RIGHT_MIDDLE); + updateCornerInfo(LEFT_MIDDLE); + updateCornerInfo(TOP_MIDDLE); + } + else + { + cornerAnnotation->ClearAllTexts(); + this->Render(); + } +} + +void infinitiViewer::setUpImageViewer() +{ + this->SetColorLevel(m_cornerInfo.win_level); + this->SetColorWindow(m_cornerInfo.win_width); + //this->GetRenderer()->ResetCamera(); + this->Render(); //first render will call resetcamera + this->SetSlice(0); + updateCornerInfoAll(); +} + diff --git a/src/src/base/seriesinstance.cpp b/src/src/base/seriesinstance.cpp new file mode 100644 index 0000000..06633e4 --- /dev/null +++ b/src/src/base/seriesinstance.cpp @@ -0,0 +1,133 @@ +锘#include "base/seriesinstance.h" +#include "qsurfaceformat.h" +#include +#include "view/DicomImageView.h" +#include "base/DicomLoader.h" +#include "vtkImageMapToWindowLevelColors.h" +#include "vtkImageProperty.h" +#include +#include +#include "vtkRenderer.h" +#include "vtkDiscretizableColorTransferFunction.h" + +qint32 SeriesInstance::image_label_size = 100; + +//---------------------------------------------------------------- +SeriesInstance::SeriesInstance(UniqueIDInfo_t* uniqueID, DicomTagInfo_t* tag_info, + vtkGenericOpenGLRenderWindow* glrenWin) : + m_pUniqueID(uniqueID), + m_pSeriesTags(tag_info), + m_glrenWin(glrenWin) +{ + m_imageViewer = vtkSmartPointer::New(); + m_image = vtkSmartPointer::New(); +} + +//---------------------------------------------------------------- +SeriesInstance::~SeriesInstance() { + +} + + +void SeriesInstance::setUpSeriesInstance() +{ + + m_imageViewer->SetInputData(m_image); + m_imageViewer->SetRenderWindow(m_glrenWin); + m_imageViewer->SetupInteractor(m_glrenWin->GetInteractor()); + + m_imageViewer->initCornerInfo(m_pSeriesTags); + m_imageViewer->setUpImageViewer(); + + + vtkCamera* cam = m_imageViewer->GetRenderer()->GetActiveCamera(); + m_extent = m_image->GetDimensions()[0] * m_pSeriesTags->spacing[0] * 0.5; + cam->SetParallelScale(m_extent); + + + //remember + m_imageViewer->GetRenderer()->GetActiveCamera()->GetViewUp(m_vup); + m_imageViewer->GetRenderer()->GetActiveCamera()->GetPosition(m_cameraPosition); + +} + + +const char* SeriesInstance::getCurImageName() const +{ + if (m_pUniqueID->open_mode == FILE_OPEN_MODE) + { + + if (m_pUniqueID->dicom_name.empty()) + return nullptr; + return m_pUniqueID->dicom_name.c_str(); + } + if (m_pUniqueID->open_mode == DIR_OPEN_MODE) + { + int index = m_imageViewer->GetSlice(); + return m_fileNames.at(index).c_str(); + } + return nullptr; +} + +const char* SeriesInstance::getCurSeriesName() const +{ + if (m_pUniqueID->dicom_name.empty()) + return nullptr; + return m_pUniqueID->dicom_name.c_str(); +} + +QPixmap SeriesInstance::GetPixmap() +{ + QImage qimage = vtkImageDataToQImage(m_image); + return QPixmap::fromImage(qimage); +} + +QImage SeriesInstance::vtkImageDataToQImage(vtkImageData* imageData) +{ + if (!imageData) { return QImage(); } + + int wl = m_pSeriesTags->WL; + int ww = m_pSeriesTags->WW; + /// \todo retrieve just the UpdateExtent + int width = imageData->GetDimensions()[0]; + int height = imageData->GetDimensions()[1]; + int slice_num = 0; + if (m_pUniqueID->open_mode == DIR_OPEN_MODE) + { + slice_num = std::atoi(m_pSeriesTags->m_SliceNumber.c_str()) / 2; + } + QImage image(width, height, QImage::Format_RGB32); + QRgb *rgbPtr = reinterpret_cast(image.bits());// +width * (height - 1); + PixelType *colorsPtr = reinterpret_cast(imageData->GetScalarPointer(0, 0, slice_num)); + //unsigned char *colorsPtr = reinterpret_cast(imageData->GetScalarPointer()); + // Loop over the vtkImageData contents. + for (int row = 0; row < height; row++) + { + for (int col = 0; col < width; col++) + { + // Swap the vtkImageData RGB values with an equivalent QColor + //qDebug() << colorsPtr[0]; + PixelType adapt_color; + if ((colorsPtr[0] - wl) < -ww/2) + { + adapt_color = static_cast(0); + } + else if ((colorsPtr[0] - wl) > ww/2) + { + adapt_color = static_cast(255.0); + } + else + { + adapt_color = static_cast((1.0*(colorsPtr[0] - wl) / ww + 0.5) * 255.0); + } + *(rgbPtr++) = QColor(adapt_color, adapt_color, adapt_color).rgb(); + + colorsPtr += imageData->GetNumberOfScalarComponents(); + } + + //rgbPtr -= width * 2; + } + + QImage resized = image.scaled(labelSizeHint(), Qt::KeepAspectRatio); + return resized; +} \ No newline at end of file diff --git a/src/src/callback/callbackhelper.cpp b/src/src/callback/callbackhelper.cpp new file mode 100644 index 0000000..6273ae5 --- /dev/null +++ b/src/src/callback/callbackhelper.cpp @@ -0,0 +1,26 @@ +#include "callback/callbackhelper.h" +#include "dcmtk/dcmdata/dcdatset.h" + +CallbackHelper::CallbackHelper(QObject *parent) + : QObject(parent) +{ +} + +CallbackHelper::~CallbackHelper() +{ +} + +void CallbackHelper::foundResult(int index, DcmDataset *response) +{ + emit sig_foundResult(index, response); +} + +void CallbackHelper::moveProgress(int progress, int total) +{ + emit sig_moveProgress(progress, total); +} + +void CallbackHelper::moveStoreProgress(int code, std::string filename) +{ + emit sig_moveStoreProgress(code, filename); +} \ No newline at end of file diff --git a/src/src/callback/cfindcallback.cpp b/src/src/callback/cfindcallback.cpp new file mode 100644 index 0000000..ae2862c --- /dev/null +++ b/src/src/callback/cfindcallback.cpp @@ -0,0 +1,26 @@ +#include "callback/cfindcallback.h" + +CFindCallback::CFindCallback() + : dcm_cfind_callback() + , CFindCallbackHelper(nullptr) +{ +} + +CFindCallback::~CFindCallback() +{ +} + +void CFindCallback::setHelper(CallbackHelper *helper) +{ + CFindCallbackHelper = helper; +} + +void CFindCallback::callback(T_DIMSE_C_FindRQ *request, int &responseCount, T_DIMSE_C_FindRSP *rsp, DcmDataset *responseIdentifiers) +{ + if (CFindCallbackHelper != nullptr) + { + //std::string name; + //responseIdentifiers->findAndGetOFString(DCM_PatientName, name); + CFindCallbackHelper->foundResult(responseCount, responseIdentifiers); + } +} diff --git a/src/src/callback/cmovecallback.cpp b/src/src/callback/cmovecallback.cpp new file mode 100644 index 0000000..d9f5a6d --- /dev/null +++ b/src/src/callback/cmovecallback.cpp @@ -0,0 +1,26 @@ +#include "callback/cmovecallback.h" + +CMoveCallback::CMoveCallback() + : dcm_cmove_callback() + , CMoveCallbackHelper(nullptr) +{ + +} + +CMoveCallback::~CMoveCallback() +{ + +} + +void CMoveCallback::setHelper(CallbackHelper *helper) +{ + CMoveCallbackHelper = helper; +} + +void CMoveCallback::callback(T_DIMSE_C_MoveRQ *request, int responseCount, T_DIMSE_C_MoveRSP *response) +{ + if (CMoveCallbackHelper != nullptr) + { + CMoveCallbackHelper ->moveProgress(response->NumberOfCompletedSubOperations, response->NumberOfCompletedSubOperations + response->NumberOfRemainingSubOperations); + } +} \ No newline at end of file diff --git a/src/src/callback/cmovestorescpcallback.cpp b/src/src/callback/cmovestorescpcallback.cpp new file mode 100644 index 0000000..d076200 --- /dev/null +++ b/src/src/callback/cmovestorescpcallback.cpp @@ -0,0 +1,46 @@ +#include "callback/cmovestorescpcallback.h" +#include "base/dicomviewerhelper.h" + +CMoveStoreSCPCallback::CMoveStoreSCPCallback(std::string outDirectory) + : dcm_cmove_storescp_callback() + , CMoveStoreScpCallbackHelper(nullptr) +{ + outDirectory_ = outDirectory; +} + +CMoveStoreSCPCallback::~CMoveStoreSCPCallback() +{ + +} + +void CMoveStoreSCPCallback::setHelper(CallbackHelper *helper) +{ + CMoveStoreScpCallbackHelper = helper; +} + +void CMoveStoreSCPCallback::callback(T_DIMSE_StoreProgress *progress, T_DIMSE_C_StoreRQ *request, char *imageFileName, DcmDataset **imageDataSet, T_DIMSE_C_StoreRSP *response, DcmDataset **statusDetail) +{ + + if (progress->state == DIMSE_StoreEnd) + { + if (imageDataSet != nullptr && *imageDataSet != nullptr) + { + std::string ofname; + OFStandard::combineDirAndFilename(ofname, outDirectory_, imageFile_, true); // use base class's imageFile_ + E_TransferSyntax xfer = (*imageDataSet)->getOriginalXfer(); + + OFCondition cond = dcmff_->saveFile(ofname.c_str(), xfer, EET_ExplicitLength, EGL_recalcGL, EPD_withoutPadding, 0, 0, EWM_fileformat); + if (cond.bad()) + { + OFStandard::deleteFile(ofname); + if (CMoveStoreScpCallbackHelper != nullptr) + CMoveStoreScpCallbackHelper->moveStoreProgress(1, imageFile_); + } + else + { + if (CMoveStoreScpCallbackHelper != nullptr) + CMoveStoreScpCallbackHelper->moveStoreProgress(0, imageFile_); + } + } + } +} diff --git a/src/src/cine/pqVCRController.cxx b/src/src/cine/pqVCRController.cxx new file mode 100644 index 0000000..267e6e9 --- /dev/null +++ b/src/src/cine/pqVCRController.cxx @@ -0,0 +1,90 @@ +#include "cine/pqVCRController.h" +// Qt includes. +#include +#include + +//----------------------------------------------------------------------------- +pqVCRController::pqVCRController(QObject* _parent, DicomImageView* v) + : QObject(_parent), m_view(v) +{ + m_timer = new QTimer(this); + connect(m_timer, SIGNAL(timeout()), this, SLOT(onTick())); + +} + +//----------------------------------------------------------------------------- +pqVCRController::~pqVCRController() +{ + m_timer->stop(); +} + +//----------------------------------------------------------------------------- +void pqVCRController::onPlay(int fps) +{ + m_cfps = fps; + int msec = 1000/ m_cfps; + m_timer->start(msec); + m_cplaying = true; +} + +//----------------------------------------------------------------------------- +void pqVCRController::onTick() +{ + + emit tick(); + //deal with real sth + if (m_view) + { + m_view->onNextFrame(); + } + +} + + +void pqVCRController::reConnect() +{ + emit Signal_FPS(m_cfps); + emit Signal_OnPlaying(m_cplaying); +} +//----------------------------------------------------------------------------- +void pqVCRController::onPause() +{ + m_timer->stop(); + m_cplaying = false; +} + +//----------------------------------------------------------------------------- +void pqVCRController::onFirstFrame() +{ + if (m_view) + { + m_view->onFirstFrame(); + } +} + +//----------------------------------------------------------------------------- +void pqVCRController::onPreviousFrame() +{ + if (m_view) + { + m_view->onPreviousFrame(); + } +} + +//----------------------------------------------------------------------------- +void pqVCRController::onNextFrame() +{ + if (m_view) + { + m_view->onNextFrame(); + } +} + +//----------------------------------------------------------------------------- +void pqVCRController::onLastFrame() +{ + if (m_view) + { + m_view->onLastFrame(); + } +} diff --git a/src/src/cine/pqVCRToolbar.cxx b/src/src/cine/pqVCRToolbar.cxx new file mode 100644 index 0000000..c871410 --- /dev/null +++ b/src/src/cine/pqVCRToolbar.cxx @@ -0,0 +1,155 @@ +#include "cine/pqVCRToolbar.h" +#include "cine/pqVCRController.h" +#include +#include "DicomImageView.h" +QString stylesheet = +"QToolBar#vcr_toolbar{border:1px solid grey;}" +"QSpinBox#spinFPS{background-color:transparent;border:0px;height:30px;width:30px;color:#a7d28c;font:bold 14px;}" +"QSpinBox#spinFPS::up-button{background-image:url(:/pqWidgets/Icon/pq/pqVcrFpsUp.png);height:15px;}" +"QSpinBox#spinFPS::down-button{background-image:url(:/pqWidgets/Icon/pq/pqVcrFpsDown.png);height:15px;}" +; + + + +pqVCRToolbar::pqVCRToolbar(QWidget *parent) :QToolBar(parent) { + ui.setupUi(this); + spin_fps = new QSpinBox(this); + spin_fps->setObjectName("spinFPS"); + spin_fps->setFont(QFont("Arial", 20, QFont::Bold)); + this->addWidget(spin_fps); + /*led_display = new QLineEdit(this); + this->addWidget(led_display);*/ + this->setEnabled(true); + this->setObjectName("vcr_toolbar"); + connect(ui.actionVCRPlay, SIGNAL(triggered()), this, SLOT(VCRPlay())); + connect(spin_fps, SIGNAL(valueChanged(int)), this, SLOT(setFPS(int))); + + this->setStyleSheet(stylesheet); + + this->spin_fps->setMinimum(1); +} + +//----------------------------------------------------------------------------- +pqVCRToolbar::~pqVCRToolbar() +{ + + + + +} +//void pqVCRToolbar::setVisible(bool visible) +//{ +// VCRHelper::setVCRToolbar(visible); +// QWidget::setVisible(visible); +//} + +void pqVCRToolbar::setImageView(DicomImageView* view) +{ + pqVCRController *ctrl = view->getVCRController(); + if (ctrl) + { + this->reConnectController(ctrl); + } +} + + + +void pqVCRToolbar::reConnectController(pqVCRController *ctrl) +{ + if (this->Controller) { + disconnect(this->Controller, SIGNAL(Signal_FPS(int)), this, SLOT(setFPS(int))); + disconnect(this->Controller, SIGNAL(Signal_OnPlaying(bool)), this, SLOT(onPlaying(bool))); + + disconnect(this, SIGNAL(Signal_Onplay(int)), this->Controller, SLOT(onPlay(int))); + disconnect(this, SIGNAL(Signal_OnPause()), this->Controller, SLOT(onPause())); + disconnect(this->Controller, SIGNAL(tick()), this, SLOT(Display())); + + disconnect(ui.actionVCRFirstFrame, SIGNAL(triggered()), this->Controller, SLOT(onFirstFrame())); + disconnect(ui.actionVCRPreviousFrame, SIGNAL(triggered()), this->Controller, SLOT(onPreviousFrame())); + disconnect(ui.actionVCRNextFrame, SIGNAL(triggered()), this->Controller, SLOT(onNextFrame())); + disconnect(ui.actionVCRLastFrame, SIGNAL(triggered()), this->Controller, SLOT(onLastFrame())); + } + //pqVCRController* controller = new pqVCRController(this); + this->Controller = ctrl; + + + connect(this->Controller, SIGNAL(Signal_FPS(int)), this, SLOT(loadFPS(int))); + connect(this->Controller, SIGNAL(Signal_OnPlaying(bool)), this, SLOT(onPlaying(bool))); + + connect(this, SIGNAL(Signal_Onplay(int)), this->Controller, SLOT(onPlay(int))); + connect(this, SIGNAL(Signal_OnPause()), this->Controller, SLOT(onPause())); + connect(this->Controller, SIGNAL(tick()), this, SLOT(Display())); + + connect(ui.actionVCRFirstFrame, SIGNAL(triggered()), this->Controller, SLOT(onFirstFrame())); + connect(ui.actionVCRPreviousFrame, SIGNAL(triggered()), this->Controller, SLOT(onPreviousFrame())); + connect(ui.actionVCRNextFrame, SIGNAL(triggered()), this->Controller, SLOT(onNextFrame())); + connect(ui.actionVCRLastFrame, SIGNAL(triggered()), this->Controller, SLOT(onLastFrame())); + + this->Controller->reConnect(); + +} + + +void pqVCRToolbar::setEnabled(bool enable) +{ + ui.actionVCRPlay->setEnabled(enable); + ui.actionVCRFirstFrame->setEnabled(enable); + ui.actionVCRPreviousFrame->setEnabled(enable); + ui.actionVCRNextFrame->setEnabled(enable); + ui.actionVCRLastFrame->setEnabled(enable); + //ui.actionVCRLoop->setEnabled(enable); +} +//----------------------------------------------------------------------------- +//void pqVCRToolbar::setTimeRanges(double start, double end) +//{ +// ui.actionVCRFirstFrame->setToolTip(QString("First Frame (%1)").arg(start, 0, 'g')); +// ui.actionVCRLastFrame->setToolTip(QString("Last Frame (%1)").arg(end, 0, 'g')); +//} + +void pqVCRToolbar::loadFPS(int fps) +{ + this->spin_fps->setValue(fps); + //this->setFPS(fps); +} +void pqVCRToolbar::setFPS(int fps) +{ + m_fps = fps; + emit Signal_Onplay(m_fps); +} + +void pqVCRToolbar::Display() +{ + //led_display->setText(QString::number(slice_num)); + slice_num++; +} +//----------------------------------------------------------------------------- + +void pqVCRToolbar::VCRPlay() +{ + m_playing = !m_playing; + onPlaying(m_playing); +} +void pqVCRToolbar::onPlaying(bool playing) +{ + m_playing = playing; + if (playing) + { + //disconnect(ui.actionVCRPlay, SIGNAL(triggered()), this->Controller, SLOT(onPlay())); + //connect(ui.actionVCRPlay, SIGNAL(triggered()), this->Controller, SLOT(onPause())); + ui.actionVCRPlay->setIcon(QIcon(":/pqWidgets/Icon/pq/pqVcrPause.png")); + ui.actionVCRPlay->setText("Pause"); + emit Signal_Onplay(m_fps); + } + else + { + //connect(ui.actionVCRPlay, SIGNAL(triggered()), this->Controller, SLOT(onPlay())); + //disconnect(ui.actionVCRPlay, SIGNAL(triggered()), this->Controller, SLOT(onPause())); + ui.actionVCRPlay->setIcon(QIcon(":/pqWidgets/Icon/pq/pqVcrPlay.png")); + ui.actionVCRPlay->setText("Play"); + emit Signal_OnPause(); + + } + + // this becomes a behavior. + // this->Implementation->Core->setSelectiveEnabledState(!playing); +} diff --git a/src/src/dialog/promptdialog.cpp b/src/src/dialog/promptdialog.cpp new file mode 100644 index 0000000..3a144a2 --- /dev/null +++ b/src/src/dialog/promptdialog.cpp @@ -0,0 +1,117 @@ +#include "dialog/promptdialog.h" +#include "dialog/promptdialogtitlebar.h" +#include +#include +#include +#include +#include + +PromptDialog::PromptDialog(QWidget *parent) + : QDialog(parent) + , m_pMainLayout(nullptr) + , m_pTitleBar(nullptr) + , m_pMsgWidget(nullptr) + , m_pMsgLayout(nullptr) + , m_pMsgLabel(nullptr) + , m_pActionWidget(nullptr) + , m_pActionLayout(nullptr) + , m_pActionSpacerItem(nullptr) + , m_pConfirmButton(nullptr) +{ + initUi(); + initSys(); +} + +PromptDialog::PromptDialog(const QString& msg, QWidget *parent) +{ + initUi(); + initSys(); + setMsg(msg); +} + +PromptDialog::~PromptDialog() +{ + +} + +void PromptDialog::setMsg(const QString& msg) +{ + m_pMsgLabel->setText(msg); +} + +void PromptDialog::initUi() +{ + this->setWindowFlags(Qt::FramelessWindowHint); + this->resize(300, 200); + this->setMinimumSize(QSize(300, 200)); + + m_pMainLayout = new QVBoxLayout(this); + m_pMainLayout->setContentsMargins(0, 0, 0, 0); + m_pTitleBar = new PromptTitleBar(this); + m_pTitleBar->setFixedHeight(30); + m_pMainLayout->addWidget(m_pTitleBar); + + m_pMsgWidget = new QWidget(this); + m_pMsgLayout = new QHBoxLayout(m_pMsgWidget); + m_pMsgLabel = new QLabel(m_pMsgWidget); + /*m_pMsgLabel->setText(tr("Message"));*/ + m_pMsgLabel->setAlignment(Qt::AlignCenter); + m_pMsgLayout->addWidget(m_pMsgLabel); + m_pMainLayout->addWidget(m_pMsgWidget); + + m_pActionWidget = new QWidget(this); + m_pActionLayout = new QHBoxLayout(m_pActionWidget); + m_pActionSpacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + m_pActionLayout->addItem(m_pActionSpacerItem); + m_pConfirmButton = new QPushButton(m_pActionWidget); + m_pConfirmButton->setText(tr("OK")); + m_pActionLayout->addWidget(m_pConfirmButton); + m_pMainLayout->addWidget(m_pActionWidget); +} + +void PromptDialog::initSys() +{ + m_pTitleBar->installEventFilter(this); + connect(m_pTitleBar, SIGNAL(sigClose()), this, SLOT(close())); + connect(m_pConfirmButton, SIGNAL(clicked()), this, SLOT(close())); +} + +void PromptDialog::close() +{ + reject(); +} + +void PromptDialog::onTitleBarDestroyed() +{ + if (m_pTitleBar == QObject::sender()) + { + m_pTitleBar = Q_NULLPTR; + } +} + +bool PromptDialog::eventFilter(QObject *obj, QEvent *event) +{ + if (m_pTitleBar == obj) + { + if (event->type() == QEvent::MouseButtonPress) + { + QRect dragArea = m_pTitleBar->geometry(); + if (dragArea.contains(mapFromGlobal(QCursor::pos())) && !this->isMaximized()) + { + m_dragState.dragging = true; + m_dragState.dragStartPosition = QCursor::pos(); + } + } + else if (event->type() == QEvent::MouseButtonRelease) + { + m_dragState.dragging = false; + } + else if (m_dragState.dragging && event->type() == QEvent::MouseMove) + { + const QPoint& curPos = QCursor::pos(); + this->move(this->geometry().topLeft() + (curPos - m_dragState.dragStartPosition)); + m_dragState.dragStartPosition = curPos; + } + } + return QDialog::eventFilter(obj, event); +} diff --git a/src/src/dialog/promptdialogtitlebar.cpp b/src/src/dialog/promptdialogtitlebar.cpp new file mode 100644 index 0000000..3b4d475 --- /dev/null +++ b/src/src/dialog/promptdialogtitlebar.cpp @@ -0,0 +1,71 @@ +#include "dialog/promptdialogtitlebar.h" +#include +#include +#include +#include +#include + +PromptTitleBar::PromptTitleBar(QWidget *parent) + : QWidget(parent) + , m_pMainLayout(nullptr) + , m_pLogoLabel(nullptr) + , m_pTitleLabel(nullptr) + , m_pSpacerItem(nullptr) + , m_pCloseButton(nullptr) +{ + initUi(); + initSys(); +} + +PromptTitleBar::~PromptTitleBar() +{ + +} + +void PromptTitleBar::setTitleText(const QString& title) +{ + m_pTitleLabel->setText(title); +} + +void PromptTitleBar::initUi() +{ + m_pMainLayout = new QHBoxLayout(this); + m_pMainLayout->setContentsMargins(5, 0, 5, 0); + + m_pLogoLabel = new QLabel(this); + QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(m_pLogoLabel->sizePolicy().hasHeightForWidth()); + m_pLogoLabel->setSizePolicy(sizePolicy); + m_pLogoLabel->setBaseSize(30, 30); + m_pLogoLabel->setPixmap(QPixmap(QString::fromUtf8(":/importwidget/Resources/import/icon.png"))); + m_pLogoLabel->setScaledContents(true); + m_pMainLayout->addWidget(m_pLogoLabel); + + m_pTitleLabel = new QLabel(this); + m_pTitleLabel->setText(tr("Message")); + m_pMainLayout->addWidget(m_pTitleLabel); + + m_pSpacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + m_pMainLayout->addItem(m_pSpacerItem); + + m_pCloseButton = new QPushButton(this); + QIcon closeIcon; + closeIcon.addPixmap(QPixmap(QString::fromUtf8(":/importwidget/Resources/import/close.png"))); + m_pCloseButton->setIcon(closeIcon); + m_pCloseButton->setIconSize(QSize(22, 22)); + m_pCloseButton->setStyleSheet("QPushButton{background:rgba(0,0,0,0);border:1px solid rgba(0,0,0,0);}"); + m_pMainLayout->addWidget(m_pCloseButton); + + //QPalette pal; + //pal.setColor(QPalette::Background, Qt::lightGray); + //this->setAutoFillBackground(true); + //this->setPalette(pal); +} + +void PromptTitleBar::initSys() +{ + connect(m_pCloseButton, SIGNAL(clicked()), this, SIGNAL(sigClose())); +} + diff --git a/src/src/dialog/qxtspanslider.cpp b/src/src/dialog/qxtspanslider.cpp new file mode 100644 index 0000000..79e5871 --- /dev/null +++ b/src/src/dialog/qxtspanslider.cpp @@ -0,0 +1,724 @@ +#include "dialog/qxtspanslider.h" +#include "dialog/qxtspanslider_p.h" +#include +#include +#include +#include +#include + +QxtSpanSliderPrivate::QxtSpanSliderPrivate() : + lower(0), + upper(0), + lowerPos(0), + upperPos(0), + offset(0), + position(0), + lastPressed(QxtSpanSlider::NoHandle), + mainControl(QxtSpanSlider::LowerHandle), + lowerPressed(QStyle::SC_None), + upperPressed(QStyle::SC_None), + movement(QxtSpanSlider::FreeMovement), + firstMovement(false), + blockTracking(false) +{ +} + +void QxtSpanSliderPrivate::initStyleOption(QStyleOptionSlider* option, QxtSpanSlider::SpanHandle handle) const +{ + const QxtSpanSlider* p = q_ptr; + p->initStyleOption(option); + option->sliderPosition = (handle == QxtSpanSlider::LowerHandle ? lowerPos : upperPos); + option->sliderValue = (handle == QxtSpanSlider::LowerHandle ? lower : upper); +} + +int QxtSpanSliderPrivate::pixelPosToRangeValue(int pos) const +{ + QStyleOptionSlider opt; + initStyleOption(&opt); + + int sliderMin = 0; + int sliderMax = 0; + int sliderLength = 0; + const QSlider* p = q_ptr; + const QRect gr = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, p); + const QRect sr = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, p); + if (p->orientation() == Qt::Horizontal) + { + sliderLength = sr.width(); + sliderMin = gr.x(); + sliderMax = gr.right() - sliderLength + 1; + } + else + { + sliderLength = sr.height(); + sliderMin = gr.y(); + sliderMax = gr.bottom() - sliderLength + 1; + } + return QStyle::sliderValueFromPosition(p->minimum(), p->maximum(), pos - sliderMin, + sliderMax - sliderMin, opt.upsideDown); +} + +void QxtSpanSliderPrivate::handleMousePress(const QPoint& pos, QStyle::SubControl& control, int value, QxtSpanSlider::SpanHandle handle) +{ + QStyleOptionSlider opt; + initStyleOption(&opt, handle); + QxtSpanSlider* p = q_ptr; + const QStyle::SubControl oldControl = control; + control = p->style()->hitTestComplexControl(QStyle::CC_Slider, &opt, pos, p); + const QRect sr = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, p); + if (control == QStyle::SC_SliderHandle) + { + position = value; + offset = pick(pos - sr.topLeft()); + lastPressed = handle; + p->setSliderDown(true); + emit p->sliderPressed(handle); + } + if (control != oldControl) + p->update(sr); +} + +void QxtSpanSliderPrivate::setupPainter(QPainter* painter, Qt::Orientation orientation, qreal x1, qreal y1, qreal x2, qreal y2) const +{ + QColor highlight = q_ptr->palette().color(QPalette::Highlight); + QLinearGradient gradient(x1, y1, x2, y2); + gradient.setColorAt(0, highlight.dark(120)); + gradient.setColorAt(1, highlight.light(108)); + painter->setBrush(gradient); + + if (orientation == Qt::Horizontal) + painter->setPen(QPen(highlight.dark(130), 0)); + else + painter->setPen(QPen(highlight.dark(150), 0)); +} + +void QxtSpanSliderPrivate::drawSpan(QStylePainter* painter, const QRect& rect) const +{ + QStyleOptionSlider opt; + initStyleOption(&opt); + const QSlider* p = q_ptr; + + // area + QRect groove = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, p); + if (opt.orientation == Qt::Horizontal) + groove.adjust(0, 0, -1, 0); + else + groove.adjust(0, 0, 0, -1); + + // pen & brush + painter->setPen(QPen(p->palette().color(QPalette::Dark).light(110), 0)); + if (opt.orientation == Qt::Horizontal) + setupPainter(painter, opt.orientation, groove.center().x(), groove.top(), groove.center().x(), groove.bottom()); + else + setupPainter(painter, opt.orientation, groove.left(), groove.center().y(), groove.right(), groove.center().y()); + + // draw groove + painter->drawRect(rect.intersected(groove)); +} + +void QxtSpanSliderPrivate::drawHandle(QStylePainter* painter, QxtSpanSlider::SpanHandle handle) const +{ + QStyleOptionSlider opt; + initStyleOption(&opt, handle); + opt.subControls = QStyle::SC_SliderHandle; + QStyle::SubControl pressed = (handle == QxtSpanSlider::LowerHandle ? lowerPressed : upperPressed); + if (pressed == QStyle::SC_SliderHandle) + { + opt.activeSubControls = pressed; + opt.state |= QStyle::State_Sunken; + } + painter->drawComplexControl(QStyle::CC_Slider, opt); +} + +void QxtSpanSliderPrivate::triggerAction(QAbstractSlider::SliderAction action, bool main) +{ + int value = 0; + bool no = false; + bool up = false; + const int min = q_ptr->minimum(); + const int max = q_ptr->maximum(); + const QxtSpanSlider::SpanHandle altControl = (mainControl == QxtSpanSlider::LowerHandle ? QxtSpanSlider::UpperHandle : QxtSpanSlider::LowerHandle); + + blockTracking = true; + + switch (action) + { + case QAbstractSlider::SliderSingleStepAdd: + if ((main && mainControl == QxtSpanSlider::UpperHandle) || (!main && altControl == QxtSpanSlider::UpperHandle)) + { + value = qBound(min, upper + q_ptr->singleStep(), max); + up = true; + break; + } + value = qBound(min, lower + q_ptr->singleStep(), max); + break; + case QAbstractSlider::SliderSingleStepSub: + if ((main && mainControl == QxtSpanSlider::UpperHandle) || (!main && altControl == QxtSpanSlider::UpperHandle)) + { + value = qBound(min, upper - q_ptr->singleStep(), max); + up = true; + break; + } + value = qBound(min, lower - q_ptr->singleStep(), max); + break; + case QAbstractSlider::SliderToMinimum: + value = min; + if ((main && mainControl == QxtSpanSlider::UpperHandle) || (!main && altControl == QxtSpanSlider::UpperHandle)) + up = true; + break; + case QAbstractSlider::SliderToMaximum: + value = max; + if ((main && mainControl == QxtSpanSlider::UpperHandle) || (!main && altControl == QxtSpanSlider::UpperHandle)) + up = true; + break; + case QAbstractSlider::SliderMove: + if ((main && mainControl == QxtSpanSlider::UpperHandle) || (!main && altControl == QxtSpanSlider::UpperHandle)) + up = true; + case QAbstractSlider::SliderNoAction: + no = true; + break; + default: + qWarning("QxtSpanSliderPrivate::triggerAction: Unknown action"); + break; + } + + if (!no && !up) + { + if (movement == QxtSpanSlider::NoCrossing) + value = qMin(value, upper); + else if (movement == QxtSpanSlider::NoOverlapping) + value = qMin(value, upper - 1); + + if (movement == QxtSpanSlider::FreeMovement && value > upper) + { + swapControls(); + q_ptr->setUpperPosition(value); + } + else + { + q_ptr->setLowerPosition(value); + } + } + else if (!no) + { + if (movement == QxtSpanSlider::NoCrossing) + value = qMax(value, lower); + else if (movement == QxtSpanSlider::NoOverlapping) + value = qMax(value, lower + 1); + + if (movement == QxtSpanSlider::FreeMovement && value < lower) + { + swapControls(); + q_ptr->setLowerPosition(value); + } + else + { + q_ptr->setUpperPosition(value); + } + } + + blockTracking = false; + q_ptr->setLowerValue(lowerPos); + q_ptr->setUpperValue(upperPos); +} + +void QxtSpanSliderPrivate::swapControls() +{ + qSwap(lower, upper); + qSwap(lowerPressed, upperPressed); + lastPressed = (lastPressed == QxtSpanSlider::LowerHandle ? QxtSpanSlider::UpperHandle : QxtSpanSlider::LowerHandle); + mainControl = (mainControl == QxtSpanSlider::LowerHandle ? QxtSpanSlider::UpperHandle : QxtSpanSlider::LowerHandle); +} + +void QxtSpanSliderPrivate::updateRange(int min, int max) +{ + Q_UNUSED(min); + Q_UNUSED(max); + // setSpan() takes care of keeping span in range + q_ptr->setSpan(lower, upper); +} + +void QxtSpanSliderPrivate::movePressedHandle() +{ + switch (lastPressed) + { + case QxtSpanSlider::LowerHandle: + if (lowerPos != lower) + { + bool main = (mainControl == QxtSpanSlider::LowerHandle); + triggerAction(QAbstractSlider::SliderMove, main); + } + break; + case QxtSpanSlider::UpperHandle: + if (upperPos != upper) + { + bool main = (mainControl == QxtSpanSlider::UpperHandle); + triggerAction(QAbstractSlider::SliderMove, main); + } + break; + default: + break; + } +} + +/*! + \class QxtSpanSlider + \inmodule QxtWidgets + \brief The QxtSpanSlider widget is a QSlider with two handles. + + QxtSpanSlider is a slider with two handles. QxtSpanSlider is + handy for letting user to choose an span between min/max. + + The span color is calculated based on QPalette::Highlight. + + The keys are bound according to the following table: + \table + \header \o Orientation \o Key \o Handle + \row \o Qt::Horizontal \o Qt::Key_Left \o lower + \row \o Qt::Horizontal \o Qt::Key_Right \o lower + \row \o Qt::Horizontal \o Qt::Key_Up \o upper + \row \o Qt::Horizontal \o Qt::Key_Down \o upper + \row \o Qt::Vertical \o Qt::Key_Up \o lower + \row \o Qt::Vertical \o Qt::Key_Down \o lower + \row \o Qt::Vertical \o Qt::Key_Left \o upper + \row \o Qt::Vertical \o Qt::Key_Right \o upper + \endtable + + Keys are bound by the time the slider is created. A key is bound + to same handle for the lifetime of the slider. So even if the handle + representation might change from lower to upper, the same key binding + remains. + + \image qxtspanslider.png "QxtSpanSlider in Plastique style." + + \bold {Note:} QxtSpanSlider inherits QSlider for implementation specific + reasons. Adjusting any single handle specific properties like + \list + \o QAbstractSlider::sliderPosition + \o QAbstractSlider::value + \endlist + has no effect. However, all slider specific properties like + \list + \o QAbstractSlider::invertedAppearance + \o QAbstractSlider::invertedControls + \o QAbstractSlider::minimum + \o QAbstractSlider::maximum + \o QAbstractSlider::orientation + \o QAbstractSlider::pageStep + \o QAbstractSlider::singleStep + \o QSlider::tickInterval + \o QSlider::tickPosition + \endlist + are taken into consideration. + */ + +/*! + \enum QxtSpanSlider::HandleMovementMode + + This enum describes the available handle movement modes. + + \value FreeMovement The handles can be moved freely. + \value NoCrossing The handles cannot cross, but they can still overlap each other. The lower and upper values can be the same. + \value NoOverlapping The handles cannot overlap each other. The lower and upper values cannot be the same. + */ + +/*! + \enum QxtSpanSlider::SpanHandle + + This enum describes the available span handles. + + \omitvalue NoHandle \omit Internal only (for now). \endomit + \value LowerHandle The lower boundary handle. + \value UpperHandle The upper boundary handle. + */ + +/*! + \fn QxtSpanSlider::lowerValueChanged(int lower) + + This signal is emitted whenever the \a lower value has changed. + */ + +/*! + \fn QxtSpanSlider::upperValueChanged(int upper) + + This signal is emitted whenever the \a upper value has changed. + */ + +/*! + \fn QxtSpanSlider::spanChanged(int lower, int upper) + + This signal is emitted whenever both the \a lower and the \a upper + values have changed ie. the span has changed. + */ + +/*! + \fn QxtSpanSlider::lowerPositionChanged(int lower) + + This signal is emitted whenever the \a lower position has changed. + */ + +/*! + \fn QxtSpanSlider::upperPositionChanged(int upper) + + This signal is emitted whenever the \a upper position has changed. + */ + +/*! + \fn QxtSpanSlider::sliderPressed(SpanHandle handle) + + This signal is emitted whenever the \a handle has been pressed. + */ + +/*! + Constructs a new QxtSpanSlider with \a parent. + */ +QxtSpanSlider::QxtSpanSlider(QWidget* parent) : QSlider(parent), d_ptr(new QxtSpanSliderPrivate()) +{ + d_ptr->q_ptr = this; + connect(this, SIGNAL(rangeChanged(int, int)), d_ptr, SLOT(updateRange(int, int))); + connect(this, SIGNAL(sliderReleased()), d_ptr, SLOT(movePressedHandle())); + + setHandleMovementMode(NoOverlapping); +} + +/*! + Constructs a new QxtSpanSlider with \a orientation and \a parent. + */ +QxtSpanSlider::QxtSpanSlider(Qt::Orientation orientation, QWidget* parent) : QSlider(orientation, parent), d_ptr(new QxtSpanSliderPrivate()) +{ + d_ptr->q_ptr = this; + connect(this, SIGNAL(rangeChanged(int, int)), d_ptr, SLOT(updateRange(int, int))); + connect(this, SIGNAL(sliderReleased()), d_ptr, SLOT(movePressedHandle())); + + setHandleMovementMode(NoOverlapping); +} + +/*! + Destructs the span slider. + */ +QxtSpanSlider::~QxtSpanSlider() +{ +} + +/*! + \property QxtSpanSlider::handleMovementMode + \brief the handle movement mode + */ +QxtSpanSlider::HandleMovementMode QxtSpanSlider::handleMovementMode() const +{ + return d_ptr->movement; +} + +void QxtSpanSlider::setHandleMovementMode(QxtSpanSlider::HandleMovementMode mode) +{ + d_ptr->movement = mode; +} + +/*! + \property QxtSpanSlider::lowerValue + \brief the lower value of the span + */ +int QxtSpanSlider::lowerValue() const +{ + return qMin(d_ptr->lower, d_ptr->upper); +} + +void QxtSpanSlider::setLowerValue(int lower) +{ + setSpan(lower, d_ptr->upper); +} + +/*! + \property QxtSpanSlider::upperValue + \brief the upper value of the span + */ +int QxtSpanSlider::upperValue() const +{ + return qMax(d_ptr->lower, d_ptr->upper); +} + +void QxtSpanSlider::setUpperValue(int upper) +{ + setSpan(d_ptr->lower, upper); +} + +/*! + Sets the span from \a lower to \a upper. + */ +void QxtSpanSlider::setSpan(int lower, int upper) +{ + const int low = qBound(minimum(), qMin(lower, upper), maximum()); + const int upp = qBound(minimum(), qMax(lower, upper), maximum()); + if (low != d_ptr->lower || upp != d_ptr->upper) + { + if (low != d_ptr->lower) + { + d_ptr->lower = low; + d_ptr->lowerPos = low; + emit lowerValueChanged(low); + } + if (upp != d_ptr->upper) + { + d_ptr->upper = upp; + d_ptr->upperPos = upp; + emit upperValueChanged(upp); + } + emit spanChanged(d_ptr->lower, d_ptr->upper); + update(); + } +} + +/*! + \property QxtSpanSlider::lowerPosition + \brief the lower position of the span + */ +int QxtSpanSlider::lowerPosition() const +{ + return d_ptr->lowerPos; +} + +void QxtSpanSlider::setLowerPosition(int lower) +{ + if (d_ptr->lowerPos != lower) + { + d_ptr->lowerPos = lower; + if (!hasTracking()) + update(); + if (isSliderDown()) + emit lowerPositionChanged(lower); + if (hasTracking() && !d_ptr->blockTracking) + { + bool main = (d_ptr->mainControl == QxtSpanSlider::LowerHandle); + d_ptr->triggerAction(SliderMove, main); + } + } +} + +/*! + \property QxtSpanSlider::upperPosition + \brief the upper position of the span + */ +int QxtSpanSlider::upperPosition() const +{ + return d_ptr->upperPos; +} + +void QxtSpanSlider::setUpperPosition(int upper) +{ + if (d_ptr->upperPos != upper) + { + d_ptr->upperPos = upper; + if (!hasTracking()) + update(); + if (isSliderDown()) + emit upperPositionChanged(upper); + if (hasTracking() && !d_ptr->blockTracking) + { + bool main = (d_ptr->mainControl == QxtSpanSlider::UpperHandle); + d_ptr->triggerAction(SliderMove, main); + } + } +} + +/*! + \reimp + */ +void QxtSpanSlider::keyPressEvent(QKeyEvent* event) +{ + QSlider::keyPressEvent(event); + + bool main = true; + SliderAction action = SliderNoAction; + switch (event->key()) + { + case Qt::Key_Left: + main = (orientation() == Qt::Horizontal); + action = !invertedAppearance() ? SliderSingleStepSub : SliderSingleStepAdd; + break; + case Qt::Key_Right: + main = (orientation() == Qt::Horizontal); + action = !invertedAppearance() ? SliderSingleStepAdd : SliderSingleStepSub; + break; + case Qt::Key_Up: + main = (orientation() == Qt::Vertical); + action = invertedControls() ? SliderSingleStepSub : SliderSingleStepAdd; + break; + case Qt::Key_Down: + main = (orientation() == Qt::Vertical); + action = invertedControls() ? SliderSingleStepAdd : SliderSingleStepSub; + break; + case Qt::Key_Home: + main = (d_ptr->mainControl == QxtSpanSlider::LowerHandle); + action = SliderToMinimum; + break; + case Qt::Key_End: + main = (d_ptr->mainControl == QxtSpanSlider::UpperHandle); + action = SliderToMaximum; + break; + default: + event->ignore(); + break; + } + + if (action) + d_ptr->triggerAction(action, main); +} + +/*! + \reimp + */ +void QxtSpanSlider::mousePressEvent(QMouseEvent* event) +{ + if (minimum() == maximum() || (event->buttons() ^ event->button())) + { + event->ignore(); + return; + } + + d_ptr->handleMousePress(event->pos(), d_ptr->upperPressed, d_ptr->upper, QxtSpanSlider::UpperHandle); + if (d_ptr->upperPressed != QStyle::SC_SliderHandle) + d_ptr->handleMousePress(event->pos(), d_ptr->lowerPressed, d_ptr->lower, QxtSpanSlider::LowerHandle); + + d_ptr->firstMovement = true; + event->accept(); +} + +/*! + \reimp + */ +void QxtSpanSlider::mouseMoveEvent(QMouseEvent* event) +{ + if (d_ptr->lowerPressed != QStyle::SC_SliderHandle && d_ptr->upperPressed != QStyle::SC_SliderHandle) + { + event->ignore(); + return; + } + + QStyleOptionSlider opt; + d_ptr->initStyleOption(&opt); + const int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &opt, this); + int newPosition = d_ptr->pixelPosToRangeValue(d_ptr->pick(event->pos()) - d_ptr->offset); + if (m >= 0) + { + const QRect r = rect().adjusted(-m, -m, m, m); + if (!r.contains(event->pos())) + { + newPosition = d_ptr->position; + } + } + + // pick the preferred handle on the first movement + if (d_ptr->firstMovement) + { + if (d_ptr->lower == d_ptr->upper) + { + if (newPosition < lowerValue()) + { + d_ptr->swapControls(); + d_ptr->firstMovement = false; + } + } + else + { + d_ptr->firstMovement = false; + } + } + + if (d_ptr->lowerPressed == QStyle::SC_SliderHandle) + { + if (d_ptr->movement == NoCrossing) + newPosition = qMin(newPosition, upperValue()); + else if (d_ptr->movement == NoOverlapping) + newPosition = qMin(newPosition, upperValue() - 1); + + if (d_ptr->movement == FreeMovement && newPosition > d_ptr->upper) + { + d_ptr->swapControls(); + setUpperPosition(newPosition); + } + else + { + setLowerPosition(newPosition); + } + } + else if (d_ptr->upperPressed == QStyle::SC_SliderHandle) + { + if (d_ptr->movement == NoCrossing) + newPosition = qMax(newPosition, lowerValue()); + else if (d_ptr->movement == NoOverlapping) + newPosition = qMax(newPosition, lowerValue() + 1); + + if (d_ptr->movement == FreeMovement && newPosition < d_ptr->lower) + { + d_ptr->swapControls(); + setLowerPosition(newPosition); + } + else + { + setUpperPosition(newPosition); + } + } + event->accept(); +} + +/*! + \reimp + */ +void QxtSpanSlider::mouseReleaseEvent(QMouseEvent* event) +{ + QSlider::mouseReleaseEvent(event); + setSliderDown(false); + d_ptr->lowerPressed = QStyle::SC_None; + d_ptr->upperPressed = QStyle::SC_None; + update(); +} + +/*! + \reimp + */ +void QxtSpanSlider::paintEvent(QPaintEvent* event) +{ + Q_UNUSED(event); + QStylePainter painter(this); + + // groove & ticks + QStyleOptionSlider opt; + d_ptr->initStyleOption(&opt); + opt.sliderValue = 0; + opt.sliderPosition = 0; + opt.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderTickmarks; + painter.drawComplexControl(QStyle::CC_Slider, opt); + + // handle rects + opt.sliderPosition = d_ptr->lowerPos; + const QRect lr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); + const int lrv = d_ptr->pick(lr.center()); + opt.sliderPosition = d_ptr->upperPos; + const QRect ur = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); + const int urv = d_ptr->pick(ur.center()); + + // span + const int minv = qMin(lrv, urv); + const int maxv = qMax(lrv, urv); + const QPoint c = QRect(lr.center(), ur.center()).center(); + QRect spanRect; + if (orientation() == Qt::Horizontal) + spanRect = QRect(QPoint(minv, c.y() - 2), QPoint(maxv, c.y() + 1)); + else + spanRect = QRect(QPoint(c.x() - 2, minv), QPoint(c.x() + 1, maxv)); + d_ptr->drawSpan(&painter, spanRect); + + // handles + switch (d_ptr->lastPressed) + { + case QxtSpanSlider::LowerHandle: + d_ptr->drawHandle(&painter, QxtSpanSlider::UpperHandle); + d_ptr->drawHandle(&painter, QxtSpanSlider::LowerHandle); + break; + case QxtSpanSlider::UpperHandle: + default: + d_ptr->drawHandle(&painter, QxtSpanSlider::LowerHandle); + d_ptr->drawHandle(&painter, QxtSpanSlider::UpperHandle); + break; + } +} diff --git a/src/src/export/dicomexporter.cpp b/src/src/export/dicomexporter.cpp new file mode 100644 index 0000000..036e4ca --- /dev/null +++ b/src/src/export/dicomexporter.cpp @@ -0,0 +1,497 @@ +#include "export/dicomexporter.h" +#include "include_vitk.h" +#include "vtkErrorCode.h" +#include "seriesinstance.h" + + +DicomExporter::DicomExporter(QObject *parent) + : QObject(parent), + exportedNumber(0) +{ +} + +void DicomExporter::execute(ExportOptions options) +{ + + exportOptions = options; + + QDir dir; + if (!dir.exists(exportOptions.exportDirectory)) + { + return;// to avoid exception + } + + initVTK(); + exportedNumber = 0; + totalCount = 0; + + caculateExportTotalCount();//caculate the total count of file to be exported + + if (exportOptions.exportFileFormat == ExportOptions::FileFormat::Dicom) + { + for (int i = 0; i < exportOptions.inputData.size(); i++) + { + //Note:When the smart pointer points to another variable, it is automatically + //released, you can move it to outside or not. + initDataReader(); + exportDicom(exportOptions.inputData.at(i)); + } + } + else + { + for (int i = 0; i < exportOptions.inputData.size(); i++) + { + initDataReader(); + exportPicture(exportOptions.inputData.at(i)); + } + } + + emit exportFinished(); +} + + +void DicomExporter::initVTK() +{ + m_glrenWinExport = vtkSmartPointer::New(); + m_glrenWinExport->OffScreenRenderingOn(); + + m_imageViewerExport = vtkSmartPointer::New(); + m_imageViewerExport->SetRenderWindow(m_glrenWinExport); + m_imageViewerExport->SetupInteractor(m_glrenWinExport->GetInteractor()); +} + +void DicomExporter::initDataReader() +{ + m_gdcmIOExport = ImageIOTypeExport::New(); + m_itkSeriesReaderExport = SeriesReaderTypeExport::New(); + m_itkSeriesReaderExport->SetImageIO(m_gdcmIOExport); + m_itkConnectorExport = ConnectorTypeExport::New(); + m_inputNamesExport = InputNamesGeneratorTypeExport::New(); +} + + +void DicomExporter::loadDicomFileAndRender(int file_type) +{ + try + { + m_itkSeriesReaderExport->Update(); + } + catch (itk::ExceptionObject &excp) { + std::cerr << "Exception thrown while reading the series" << std::endl; + std::cerr << excp << std::endl; + } + + m_itkConnectorExport->SetInput(m_itkSeriesReaderExport->GetOutput()); + m_itkConnectorExport->Update(); + + m_imageViewerExport->SetInputData(m_itkConnectorExport->GetOutput()); + + m_imageViewerExport->Render(); + m_imageViewerExport->SetSlice(0); + + //m_imageViewerExport->GetRenderer()->ResetCamera(); + + + infinitiViewer *viewer = exportOptions.serie->getImageViewer2(); + + if (exportOptions.windowLevel != -1) + { + m_imageViewerExport->SetColorLevel(exportOptions.windowLevel); + } + + if (exportOptions.windowWidth != -1) + { + m_imageViewerExport->SetColorWindow(exportOptions.windowWidth); + } + + if (exportOptions.cornerAnnotation != ExportOptions::CornerAnnotation::Disabled) + { + if (AnnoHelper::IsAnno()) { + vtkCornerAnnotation* ann = m_imageViewerExport->GetvtkCornerAnnotation(); + ann->SetMaximumFontSize(20); + ann->CopyAllTextsFrom(viewer->GetvtkCornerAnnotation()); + ann->SetText(BOTTOM_LEFT, ""); + + std::string lbl_ser_num; + std::string ser_num; + itk::GDCMImageIO::GetLabelFromTag(USER_CONFIG::TAG_SERIES_NUMBER, lbl_ser_num); + m_gdcmIOExport->GetValueFromTag(USER_CONFIG::TAG_SERIES_NUMBER, ser_num); + m_imageViewerExport->initTopLeftCornerInfo(lbl_ser_num, ser_num); + } + } + + + double vup[3]; + double vup2[3]; + vtkCamera *oriCamera = viewer->GetRenderer()->GetActiveCamera(); + double scale = exportOptions.serie->GetExtent(); + oriCamera->GetViewUp(vup); + + vtkCamera *expCamera = m_imageViewerExport->GetRenderer()->GetActiveCamera(); + expCamera->SetParallelScale(scale); + expCamera->GetViewUp(vup2); + expCamera->SetViewUp(vup); + + if (FlipExportHelper::GetFlip()) + { + expCamera->Azimuth(180); + m_imageViewerExport->GetRenderer()->ResetCameraClippingRange(); + + } + m_imageViewerExport->GetRenderWindow()->Render(); +} + + + + +void DicomExporter::doExport(int file_type) +{ + if (file_type == SingleFile) + { + exportedNumber++;//image number starts from 1 + QString fileName = exportOptions.exportDirectory + "/" + exportOptions.fileNamePrefix + QString::number(exportedNumber) + getFileExtention(); + writeToFile(fileName); + emit exportProgress(totalCount, exportedNumber); + } + else if(file_type == DirType) + { + int m_MinSliceExport = m_imageViewerExport->GetSliceMin(); + int m_MaxSliceExport = m_imageViewerExport->GetSliceMax(); + + for (int i = m_MinSliceExport; i <= m_MaxSliceExport; i++) + { + m_imageViewerExport->SetSlice(i); + //m_imageViewerExport->Render(); + + exportedNumber++;//image number starts from 1 + QString fileName = exportOptions.exportDirectory + "/" + exportOptions.fileNamePrefix + QString::number(exportedNumber) + getFileExtention(); + writeToFile(fileName); + + emit exportProgress(totalCount, exportedNumber); + } + } + else + { + return; + } +} + + + +void DicomExporter::writeToFile(const QString& fileName) +{ + // Screenshot + vtkSmartPointer windowToImageFilter = vtkSmartPointer::New(); + + windowToImageFilter->SetInput(m_glrenWinExport); +#if VTK_MAJOR_VERSION >= 8 || VTK_MAJOR_VERSION == 8 && VTK_MINOR_VERSION >= 90 + windowToImageFilter->SetScale(2); // image quality +#else + windowToImageFilter->SetMagnification(2); //image quality +#endif + + windowToImageFilter->Update(); + + + switch (exportOptions.exportFileFormat) + { + case ExportOptions::FileFormat::Bmp: + writeToBmpFile(windowToImageFilter, fileName); + break; + + case ExportOptions::FileFormat::Jpeg: + writeToJpgFile(windowToImageFilter, fileName); + break; + + case ExportOptions::FileFormat::Png: + writeToPngFile(windowToImageFilter, fileName); + break; + + case ExportOptions::FileFormat::Tiff: + writeToTiffFile(windowToImageFilter, fileName); + break; + + default: + break; + } +} + +QString DicomExporter::getFileExtention() +{ + QString fileExtention = ".png"; + + switch (exportOptions.exportFileFormat) + { + case ExportOptions::FileFormat::Bmp: + fileExtention = ".bmp"; + break; + + case ExportOptions::FileFormat::Jpeg: + fileExtention = ".jpg"; + break; + + case ExportOptions::FileFormat::Png: + fileExtention = ".png"; + break; + + case ExportOptions::FileFormat::Tiff: + fileExtention = ".tiff"; + break; + + case ExportOptions::FileFormat::Dicom: + fileExtention = ".dcm"; + break; + + default: + break; + } + + return fileExtention; +} + +void DicomExporter::writeToPngFile(vtkSmartPointer windowToImageFilter, const QString& fileName) +{ + QByteArray bafileName = fileName.toLocal8Bit(); + + vtkSmartPointer writer = vtkSmartPointer::New(); + writer->SetFileName(bafileName.data()); + writer->SetInputConnection(windowToImageFilter->GetOutputPort()); + writer->Write(); +} + +void DicomExporter::writeToBmpFile(vtkSmartPointer windowToImageFilter, const QString& fileName) +{ + QByteArray bafileName = fileName.toLocal8Bit(); + + vtkSmartPointer writer = vtkSmartPointer::New(); + writer->SetFileName(bafileName.data()); + writer->SetInputConnection(windowToImageFilter->GetOutputPort()); + writer->Write(); +} + +void DicomExporter::writeToJpgFile(vtkSmartPointer windowToImageFilter, const QString& fileName) +{ + QByteArray bafileName = fileName.toLocal8Bit(); + + vtkSmartPointer writer = vtkSmartPointer::New(); + writer->SetFileName(bafileName.data()); + writer->SetInputConnection(windowToImageFilter->GetOutputPort()); + writer->Write(); +} + +void DicomExporter::writeToTiffFile(vtkSmartPointer windowToImageFilter, const QString& fileName) +{ + QByteArray bafileName = fileName.toLocal8Bit(); + + vtkSmartPointer writer = vtkSmartPointer::New(); + writer->SetFileName(bafileName.data()); + writer->SetInputConnection(windowToImageFilter->GetOutputPort()); + writer->Write(); +} + +void DicomExporter::exportPicture(const QString& fileName) +{ + int myFileType = NonType; + + QFileInfo fileInfo(fileName); + QDir dir(fileName); + + if (fileInfo.isFile()) + { + m_itkSeriesReaderExport->SetFileName(fileName.toStdString()); + + myFileType = SingleFile; + } + else if (dir.exists()) + { + m_inputNamesExport->SetInputDirectory(fileName.toStdString()); + const SeriesReaderTypeExport::FileNamesContainer & filenames = m_inputNamesExport->GetInputFileNames(); + m_itkSeriesReaderExport->SetFileNames(filenames); + + myFileType = DirType; + } + + if (myFileType != NonType) + { + loadDicomFileAndRender(myFileType); + doExport(myFileType); + } +} + +void DicomExporter::exportDicom(const QString& fileName) +{ + bool isFileValid = false; + + QFileInfo fileInfo(fileName); + QDir dir(fileName); + + if (fileInfo.isFile()) + { + m_itkSeriesReaderExport->SetFileName(fileName.toStdString()); + + exportSingleDicomFile(fileName); + } + else if (dir.exists()) + { + m_inputNamesExport->SetInputDirectory(fileName.toStdString()); + const SeriesReaderTypeExport::FileNamesContainer & filenames = m_inputNamesExport->GetInputFileNames(); + m_itkSeriesReaderExport->SetFileNames(filenames); + + exportDicomDirectory(fileName); + } +} + +void DicomExporter::exportSingleDicomFile(const QString& fileName) +{ + try + { + m_itkSeriesReaderExport->UpdateLargestPossibleRegion(); + m_itkSeriesReaderExport->Update(); + } + catch (itk::ExceptionObject &excp) { + std::cerr << "Exception thrown while reading the series" << std::endl; + std::cerr << excp << std::endl; + } + + exportedNumber++;//image number starts from 1 + QString exportedName = exportOptions.exportDirectory + "\\" + exportOptions.fileNamePrefix + QString::number(exportedNumber) + ".dcm"; + QByteArray bafileName = exportedName.toLocal8Bit(); + + + SeriesWriterType::Pointer seriesWriter = SeriesWriterType::New(); + seriesWriter->SetImageIO(m_gdcmIOExport); + seriesWriter->SetInput(m_itkSeriesReaderExport->GetOutput()); + seriesWriter->SetFileName(std::string(bafileName)); + + SeriesReaderTypeExport::DictionaryArrayRawPointer dicArray = m_itkSeriesReaderExport->GetMetaDataDictionaryArray(); + modifyDicomTags(dicArray); + seriesWriter->SetMetaDataDictionaryArray(dicArray); + + try + { + seriesWriter->Update(); + + emit exportProgress(totalCount, exportedNumber); + } + catch (itk::ExceptionObject & excp) + { + std::cerr << "Exception thrown while writing the series " << std::endl; + std::cerr << excp << std::endl; + } +} + +void DicomExporter::exportDicomDirectory(const QString& fileName) +{ + try + { + m_itkSeriesReaderExport->Update(); + } + catch (itk::ExceptionObject &excp) { + std::cerr << "Exception thrown while reading the series" << std::endl; + std::cerr << excp << std::endl; + } + + m_inputNamesExport->SetOutputDirectory(exportOptions.exportDirectory.toStdString()); + + itk::SerieUIDContainer newOutNames; + itk::SerieUIDContainer oldOutputNames = m_inputNamesExport->GetOutputFileNames(); + for (int i = 0; i < oldOutputNames.size(); i++) + { + std::string originName = oldOutputNames.at(i); + QFileInfo fileinfo(QString::fromStdString(originName)); + QString fName = fileinfo.fileName(); + QString fPath = fileinfo.absolutePath(); + + exportedNumber++;//image number starts from 1 + QString newName = fPath + "\\" + exportOptions.fileNamePrefix + QString::number(exportedNumber) + ".dcm"; + QByteArray bafileName = newName.toLocal8Bit(); + + newOutNames.push_back(std::string(bafileName)); + } + + SeriesWriterType::Pointer seriesWriter = SeriesWriterType::New(); + seriesWriter->SetImageIO(m_gdcmIOExport); + seriesWriter->SetInput(m_itkSeriesReaderExport->GetOutput()); + seriesWriter->SetFileNames(newOutNames); + + SeriesReaderTypeExport::DictionaryArrayRawPointer dicArray = m_itkSeriesReaderExport->GetMetaDataDictionaryArray(); + modifyDicomTags(dicArray); + seriesWriter->SetMetaDataDictionaryArray(dicArray); + + try + { + emit exportProgress(totalCount, exportedNumber / 2); + + seriesWriter->Update(); + + emit exportProgress(totalCount, exportedNumber); + } + catch (itk::ExceptionObject & excp) + { + std::cerr << "Exception thrown while writing the series " << std::endl; + std::cerr << excp << std::endl; + } +} + +void DicomExporter::modifyDicomTags(SeriesReaderTypeExport::DictionaryArrayRawPointer dicArray) +{ + for (int i = 0; i < (*dicArray).size(); i++) + { + itk::MetaDataDictionary *dic = (*dicArray).at(i); + + if (exportOptions.windowLevel != -1) + { + std::string entryId(TAG_WindowLevel); + itk::EncapsulateMetaData(*dic, entryId, std::to_string(exportOptions.windowLevel)); + } + + if (exportOptions.windowWidth != -1) + { + std::string entryId(TAG_WindowWidth); + itk::EncapsulateMetaData(*dic, entryId, std::to_string(exportOptions.windowWidth)); + } + + if (exportOptions.isAnonymization) + { + itk::EncapsulateMetaData(*dic, TAG_InstitutionName, "anonymized"); + itk::EncapsulateMetaData(*dic, TAG_InstitutionAddress, "anonymized"); + itk::EncapsulateMetaData(*dic, TAG_ReferringPhysicianName, "anonymized"); + itk::EncapsulateMetaData(*dic, TAG_OperatorsName, "anonymized"); + itk::EncapsulateMetaData(*dic, TAG_PatientAddress, "anonymized"); + itk::EncapsulateMetaData(*dic, TAG_PatientTelephoneNumbers, "anonymized"); + itk::EncapsulateMetaData(*dic, TAG_OtherPatientNames, "anonymized"); + itk::EncapsulateMetaData(*dic, TAG_OtherPatientIDs, "anonymized"); + itk::EncapsulateMetaData(*dic, TAG_OtherPatientIDsSequence, ""); + itk::EncapsulateMetaData(*dic, TAG_PatientName, "anonymized"); + itk::EncapsulateMetaData(*dic, TAG_PatientBirthDate, "19000101"); + itk::EncapsulateMetaData(*dic, TAG_PatientSex, "NA"); + itk::EncapsulateMetaData(*dic, TAG_PatientAge, "NA"); + itk::EncapsulateMetaData(*dic, TAG_PatientID, "anonymized"); + itk::EncapsulateMetaData(*dic, TAG_AccessionNumber, "anonymized"); + } + + }//for end +} + +void DicomExporter::caculateExportTotalCount() +{ + for (int i = 0; i < exportOptions.inputData.size(); i++) + { + QString fileName = exportOptions.inputData.at(i); + + QFileInfo fileInfo(fileName); + QDir dir(fileName); + + if (fileInfo.isFile()) + { + totalCount++; + } + else if (dir.exists()) + { + QStringList filter; + QFileInfoList fileList = dir.entryInfoList(filter); + totalCount += fileList.count(); + totalCount -= 2; + } + } +} diff --git a/src/src/export/dicomexporterthread.cpp b/src/src/export/dicomexporterthread.cpp new file mode 100644 index 0000000..5e12a35 --- /dev/null +++ b/src/src/export/dicomexporterthread.cpp @@ -0,0 +1,25 @@ +#include "export/dicomexporterthread.h" + +DicomExporterThread::DicomExporterThread(ExportOptions options, QObject *parent) + : QThread(parent) +{ + exportOptions = options; + + //init(); +} + +//void DicomExporterThread::init() +//{ +//} + +void DicomExporterThread::run() +{ + exporter = new DicomExporter(); + connect(exporter, SIGNAL(exportProgress(int, int)), this, SIGNAL(exportProgress(int, int))); + connect(exporter, SIGNAL(exportFinished()), this, SIGNAL(exportFinished())); + + exporter->execute(exportOptions); + + delete exporter; +} + diff --git a/src/src/export/exportdialog.cpp b/src/src/export/exportdialog.cpp new file mode 100644 index 0000000..c57cbb2 --- /dev/null +++ b/src/src/export/exportdialog.cpp @@ -0,0 +1,183 @@ +#include "export/exportdialog.h" +#include "ui_exportdialog.h" + +ExportDialog::ExportDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::ExportDialog) +{ + ui->setupUi(this); + ui->progressBar->setVisible(false); + init(); + +} + +ExportDialog::~ExportDialog() +{ + delete ui; +} + +void ExportDialog::init() +{ + connect(ui->btnExport, SIGNAL(clicked()), this, SLOT(onBtnExportClicked())); + connect(ui->btnCancel, SIGNAL(clicked()), this, SLOT(onBtnCancelClicked())); + connect(ui->btnSelectFolder, SIGNAL(clicked()), this, SLOT(onBtnSelectFolderClicked())); +} + +void ExportDialog::initUI() +{ + ui->progressBar->setVisible(false); +} + +void ExportDialog::onBtnSelectFolderClicked() +{ + QDir dir; + QString dirpath = QFileDialog::getExistingDirectory(this, tr("select a directory"), "./", QFileDialog::ShowDirsOnly); + if (!dir.exists(dirpath)) + { + return; + } + + ui->edtExportFolder->setText(dirpath); +} + +void ExportDialog::onBtnExportClicked() +{ + QDir dir; + if (!dir.exists(ui->edtExportFolder->text())) + { + QString dirpath = QFileDialog::getExistingDirectory(this, tr("select a directory"), "./", QFileDialog::ShowDirsOnly); + if (!dir.exists(dirpath)) + { + return; + } + + ui->edtExportFolder->setText(dirpath); + } + + ui->btnExport->setEnabled(false); + ui->progressBar->setVisible(true); + ui->progressBar->setValue(0); + + ExportOptions options; + + //export files + if (ui->rbCurrentImage->isChecked())//current image + { + //SeriesInstance *instance = viewContainerWidget->getCurrentSeries(); + if (cur_view->HasSeries()) + { + QString imageName(cur_view->getSeriesInstance()->getCurImageName()); + options.inputData.push_back(imageName); + } + } + else if (ui->rbCurrentSeries->isChecked())//current series + { + //SeriesInstance *instance = viewContainerWidget->getCurrentSeries(); + if (cur_view->HasSeries()) + { + QString serieName(cur_view->getSeriesInstance()->getCurSeriesName()); + options.inputData.push_back(serieName); + } + } + else if (ui->rbAllOpendSeries->isChecked())// all opened series + { + InstancesVecType instanceVec; + DicomLoader::GetInstance()->getOpenedInstancesVec(instanceVec); + for (int i = 0; i < instanceVec.size(); i++) + { + SeriesInstance *instance = instanceVec.at(i); + if (instance != nullptr) + { + options.inputData.push_back(instance->getCurSeriesName()); + } + } + } + + if (ui->rbJPEG->isChecked()) + { + options.exportFileFormat = ExportOptions::FileFormat::Jpeg; + } + else if (ui->rbBMP->isChecked()) + { + options.exportFileFormat = ExportOptions::FileFormat::Bmp; + } + else if (ui->rbPNG->isChecked()) + { + options.exportFileFormat = ExportOptions::FileFormat::Png; + } + else if (ui->rbTIFF->isChecked()) + { + options.exportFileFormat = ExportOptions::FileFormat::Tiff; + } + else if (ui->rbDICOM->isChecked()) + { + options.exportFileFormat = ExportOptions::FileFormat::Dicom; + } + + options.exportDirectory = ui->edtExportFolder->text(); + + if (ui->rbFull->isChecked()) + { + options.cornerAnnotation = ExportOptions::CornerAnnotation::Full; + } + else if (ui->rbBasic->isChecked()) + { + options.cornerAnnotation = ExportOptions::CornerAnnotation::Basic; + } + else if (ui->rbDisabled->isChecked()) + { + options.cornerAnnotation = ExportOptions::CornerAnnotation::Disabled; + } + + options.fileNamePrefix = ui->edtFileNamePrefix->text(); + + if (!ui->rbFull->isChecked()) + { + options.isAnonymization = true; + } + else + { + options.isAnonymization = false; + } + + options.windowLevel = cur_view->getImageViewer()->GetColorLevel(); + options.windowWidth = cur_view->getImageViewer()->GetColorWindow(); + options.serie = cur_view->getSeriesInstance(); + DicomExporterThread *worker = new DicomExporterThread(options); + //disconnect(worker, SIGNAL(exportProgress(int, int)), this, SLOT(onExportProgress(int, int))); + connect(worker, SIGNAL(exportProgress(int, int)), this, SLOT(onExportProgress(int, int))); + + //disconnect(worker, SIGNAL(exportFinished()), this, SLOT(onExportFinished())); + connect(worker, SIGNAL(exportFinished()), this, SLOT(onExportFinished())); + connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); + + worker->start(); +} + +void ExportDialog::onBtnCancelClicked() +{ + reject(); +} + +void ExportDialog::onExportProgress(int totalCount, int progress) +{ + ui->progressBar->setMinimum(0); + ui->progressBar->setMaximum(totalCount); + ui->progressBar->setValue(progress); +} + +void ExportDialog::onExportFinished() +{ + ui->progressBar->setVisible(false); + ui->btnExport->setEnabled(true); +} + +void ExportDialog::setCurView(DicomImageView *view) +{ + cur_view = view; +} + +//void ExportDialog::setViewContainer(ViewContainerWidget *widget) +//{ +// viewContainerWidget = widget; +//} diff --git a/src/src/export/exportoptions.cpp b/src/src/export/exportoptions.cpp new file mode 100644 index 0000000..16d2502 --- /dev/null +++ b/src/src/export/exportoptions.cpp @@ -0,0 +1,14 @@ +#include "exportoptions.h" +ExportOptions::ExportOptions(): + inputData(), + windowWidth(-1), + windowLevel(-1), + exportFileFormat(ExportOptions::FileFormat::Png), + isAnonymization(false), + cornerAnnotation(ExportOptions::CornerAnnotation::Full), + fileNamePrefix(""), + isViewFilesWhenFinished(false), + pictureSizePolicy(ExportOptions::PictureSize::Default) +{ + +} diff --git a/src/src/global/QGlobals.cpp b/src/src/global/QGlobals.cpp new file mode 100644 index 0000000..c58f7dd --- /dev/null +++ b/src/src/global/QGlobals.cpp @@ -0,0 +1,23 @@ +#pragma once +#include "include_all.h" +#include "include_vitk.h" + +int VCRHelper:: toolbar_coeff_inv =0; +int VCRHelper:: thumbnailbar_coeff_inv =0; + + +int FontSizeHelper::font_size = font_fixed; +int FontSizeHelper::desktop_height = 1920; +int FontSizeHelper::desktop_width = 1080; + +bool AnnoHelper::privacyOn = false; +bool AnnoHelper::annotOn = true; + + +bool FlipExportHelper::flip = false; + +SyncState SyncHelper::_syc_state = SyncState::DIS_SYNC; +bool SyncHelper::_syc_item[SYNC_STATE_NUM][SYNC_ITEM_NUM] = {true,true,true,true,true,true,false,false,false}; +bool SyncHelper::_sync_flag =false; //for just zoom and pan +const QString SyncHelper::SyncStateName[SYNC_STATE_NUM] = { "AUTO_SYNC","MANUAL_SYNC","DIS_SYNC" }; +const QString SyncHelper::SyncItemName[SYNC_ITEM_NUM] = { "SLICE_LOC","ZOOM_PAN","WIDTH_LEVEL" }; \ No newline at end of file diff --git a/src/src/main.cpp b/src/src/main.cpp new file mode 100644 index 0000000..9bdfe26 --- /dev/null +++ b/src/src/main.cpp @@ -0,0 +1,42 @@ +#include +#include "QDicomViewer.h" +#include +#include +#include "view/myQVTKOpenGLNativeWidget.h" +#include + +VTK_MODULE_INIT(vtkRenderingOpenGL2); +VTK_MODULE_INIT(vtkInteractionStyle); +VTK_MODULE_INIT(vtkRenderingFreeType) +VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2) +//#define vtkRenderingCore_AUTOINIT 4(vtkInteractionStyle,vtkRenderingFreeType,vtkRenderingFreeType,vtkRenderingOpenGL2) + +#ifndef _DEBUG +#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" ) +#endif // _Release + + +int main(int argc, char* argv[]) +{ + + QTextCodec* codec = QTextCodec::codecForName("GB2312"); + QTextCodec::setCodecForLocale(codec); + QSurfaceFormat::setDefaultFormat(QVTKOpenGLNativeWidget::defaultFormat()); + + QApplication a(argc, argv); + a.setWindowIcon(QIcon(":/InfiniteViewer/Icon/logo.png")); + //a.setStyleSheet(style); + + QFont font; + font.setFamily(QString::fromUtf8("Arial")); + //QFont font("Microsoft YaHei" + //QFont font("Courier"); + //font.setStyleHint(QFont::); + QApplication::setFont(font); + + //qDebug() << desktop_width << desktop_height; + + QDicomViewer w; + w.show(); + return a.exec(); +} diff --git a/src/src/measure/ActorDraggableInteractorStyle.cpp b/src/src/measure/ActorDraggableInteractorStyle.cpp new file mode 100644 index 0000000..bc63b9c --- /dev/null +++ b/src/src/measure/ActorDraggableInteractorStyle.cpp @@ -0,0 +1,598 @@ +// +// Created by 87714 on 2021/6/19. +// + +#include +#include +#include +#include +#include +#include +#include "ActorDraggableInteractorStyle.h" +#include "vtkRenderWindowInteractor.h" +#include "vtkProp.h" +#include "vtkPropCollection.h" +#include "vtkImageSlice.h" +#include "vtkAssemblyPath.h" +#include "vtkRenderer.h" +#include "vtkImageSliceMapper.h" +#include "vtkCamera.h" + +#include "DraggableActor.h" +#include "vtkPointPicker.h" +#include "vtkCellPicker.h" +#include "vtkCallbackCommand.h" +#include "Measure.h" + +#include "vtkImageProperty.h" + +#include "QGlobals.h" +vtkStandardNewMacro(ActorDraggableInteractorStyle); +ActorDraggableInteractorStyle::ActorDraggableInteractorStyle() { + this->AddObserver(vtkCommand::InteractionEvent,this,&ActorDraggableInteractorStyle::DispatchEvent); +#ifdef _DEBUG + this->AddObserver(DraggableStyleEvents::EndDollyEvent, this, &ActorDraggableInteractorStyle::TestOutPut); + this->AddObserver(vtkCommand::EventIds::EndPanEvent, this, &ActorDraggableInteractorStyle::TestOutPut); + this->AddObserver(vtkCommand::EventIds::EndRotateEvent, this, &ActorDraggableInteractorStyle::TestOutPut); + this->AddObserver(vtkCommand::EventIds::EndWindowLevelEvent, this, &ActorDraggableInteractorStyle::TestOutPut); + this->AddObserver(DraggableStyleEvents::SlicedEvent, this, &ActorDraggableInteractorStyle::TestOutPut); +#endif +} + +ActorDraggableInteractorStyle::~ActorDraggableInteractorStyle() { + +} + +template +void vtkValueMessageTemplate(vtkImageData* image, int* position, std::string& message) +{ + T* tuple = ((T*)image->GetScalarPointer(position)); + if (!tuple) + { + return; + } + int components = image->GetNumberOfScalarComponents(); + for (int c = 0; c < components; ++c) + { + message += vtkVariant(tuple[c]).ToString(); + if (c != (components - 1)) + { + message += ", "; + } + } + //message += " )"; +} + +void ActorDraggableInteractorStyle::OnLeftButtonUp() { + switch (this->State) { + case VTKIS_DRAG: + DraggableActor::SafeDownCast(dragProp)->ApplyTransform(); + this->EndDrag(); + break; + case VTKIS_MEASURE: + measure->SetPlacing(measure->onMeasureLeftButtonUp(this->Interactor)); + if (!measure->isMeasurePlacing()) { + this->EndMeasure(); + auto temp = measure; + measure = measure->GetNextMeasure(); + if (!temp->Valid()){ + temp->ForceDelete(); + temp= nullptr; + } + } + break; + case VTKIS_COLORMAP: + this->EndColorMapping(); + break; + } + vtkInteractorStyleImage::OnLeftButtonUp(); +} +void ActorDraggableInteractorStyle::ColorMapping() +{ + + vtkRenderWindowInteractor *rwi = this->Interactor; + this->ScalarCurrentPosition[1] = rwi->GetEventPosition()[1]; + + //pos[0] = (this->ScalarCurrentPosition[0] - this->ScalarStartPosition[0]); + double total = 0.01*((this->ScalarCurrentPosition[1] - this->ScalarStartPosition[1])/scalarSensitivity); + double left = total - ConsumedOpacity; + //printf("ColorMapping:%d,%d,%.2f \r\n", ScalarCurrentPosition[1], ScalarStartPosition[1],left); + if (abs(left) >= 0.01) { + OpacityTrigger = true; + this->InvokeEvent(ScalarOpacityEvent, &left); + ConsumedOpacity += left; + } +} + + +void ActorDraggableInteractorStyle::EndColorMapping() +{ + if (false == OpacityTrigger) + { + this->InvokeEvent(ScalarShiftEvent, nullptr); + } + if (this->State != VTKIS_COLORMAP) + { + return; + } + this->StopState(); +} + + +void ActorDraggableInteractorStyle::Drag() { + int* pos = this->Interactor->GetEventPosition(); + this->FindPokedRenderer(pos[0],pos[1]); + DraggableActor::SafeDownCast(dragProp)->Transform(pos[0] - DragStartOrigin[0], pos[1] - DragStartOrigin[1]); + this->Interactor->Render(); +} + + +void ActorDraggableInteractorStyle::MeasurePlace() { + measure->onMeasureMouseMove(this->Interactor); +} + +void ActorDraggableInteractorStyle::NoneStatePick() { + + if (AnnoHelper::IsAnno()) + { + int* pos = this->Interactor->GetEventPosition(); + this->FindPokedRenderer(pos[0], pos[1]); + int result = picker->PickProp(pos[0], pos[1], this->CurrentRenderer); + if (result) + { + + vtkProp* obj = picker->GetViewProp(); + if (scalarProp != obj && scalarProp) { + scalarProp = nullptr; + } + if (dragProp != obj && dragProp) { + + DraggableActor::SafeDownCast(dragProp)->MouseLeave(); + dragProp = nullptr; + } + if (obj->IsA("vtkImageSlice")) + { + //active pick pixel + vtkNew p; + p->SetPickFromList(1); + p->AddPickList(obj); + p->Pick(pos[0], pos[1], 0.0, this->CurrentRenderer); + int* ijk = p->GetPointIJK(); + std::string message = "X:"; + message += vtkVariant(ijk[0]).ToString(); + message += " Y:"; + message += vtkVariant(ijk[1]).ToString(); + message += ", "; + message += vtkVariant(ijk[2]).ToString(); + message += " Val:"; + vtkImageData* imageData = vtkImageSlice::SafeDownCast(obj)->GetMapper()->GetInput(); + switch (imageData->GetScalarType()) + { + vtkTemplateMacro((vtkValueMessageTemplate(imageData, ijk, message))); + default: + return; + } + this->CornerAnnotation->SetText(BOTTOM_LEFT, message.c_str()); + this->Interactor->Render(); + return; + } + if (obj->IsA("DraggableActor")) + { + //active highlight + if (dragProp != obj) { + dragProp = obj; + DraggableActor::SafeDownCast(dragProp)->MouseEntered(); + } + } + if (obj->IsA("vtkScalarBarActor")) + { + if (scalarProp != obj) + { + scalarProp = obj; + } + } + + } + //nopicked but moving + else if (dragProp) { + if (scalarProp) { + scalarProp = nullptr; + } + DraggableActor::SafeDownCast(dragProp)->MouseLeave(); + } + else { + if (scalarProp) { + scalarProp = nullptr; + } + dragProp = nullptr; + this->CornerAnnotation->SetText(BOTTOM_LEFT, ""); + } + } + this->CornerAnnotation->SetText(BOTTOM_LEFT, ""); + this->Interactor->Render(); + +} + +void ActorDraggableInteractorStyle::OnMouseMove() { + + int x = this->Interactor->GetEventPosition()[0]; + int y = this->Interactor->GetEventPosition()[1]; + + switch (this->State) { + case VTKIS_MEASURE: + MeasurePlace(); + //鐓ф妱婧愮爜锛屼笉鏄緢娓呮杩欏彞璇濈殑浣滅敤锛屽彲鑳芥湁鍔犻熷鐞嗙敤鎴疯緭鍏ヤ簨浠剁殑浣滅敤锛堣繛缁璉nteractionEvent澶勭悊杩炵画鎿嶄綔锛岄伩鍏嶅閮ㄥ垽鏂級 + this->InvokeEvent(vtkCommand::InteractionEvent, nullptr); + break; + case VTKIS_DRAG: + Drag(); + this->InvokeEvent(vtkCommand::InteractionEvent, nullptr); + break; + case VTKIS_NONE: + NoneStatePick(); + this->InvokeEvent(vtkCommand::InteractionEvent, nullptr); + break; + case VTKIS_COLORMAP: + this->FindPokedRenderer(x, y); + this->ColorMapping(); + this->InvokeEvent(vtkCommand::InteractionEvent, nullptr); + break; + + } + vtkInteractorStyleImage::OnMouseMove(); +} + +void ActorDraggableInteractorStyle::WindowLevel() +{ + vtkRenderWindowInteractor *rwi = this->Interactor; + + this->WindowLevelCurrentPosition[0] = rwi->GetEventPosition()[0]; + this->WindowLevelCurrentPosition[1] = rwi->GetEventPosition()[1]; + + if (this->HandleObservers && + this->HasObserver(vtkCommand::WindowLevelEvent)) + { + this->InvokeEvent(vtkCommand::WindowLevelEvent, this); + } + if (this->CurrentImageProperty) + { + int *size = this->CurrentRenderer->GetSize(); + + double window = this->WindowLevelInitial[0]; + double level = this->WindowLevelInitial[1]; + + // Compute normalized delta + + double dx = (this->WindowLevelCurrentPosition[0] - + this->WindowLevelStartPosition[0]) * 4.0 / size[0]; + double dy = (this->WindowLevelStartPosition[1] - + this->WindowLevelCurrentPosition[1]) * 4.0 / size[1]; + + // Scale by current values + + if (fabs(window) > 0.01) + { + dx = dx * window; + } + else + { + dx = dx * (window < 0 ? -0.01 : 0.01); + } + if (fabs(level) > 0.01) + { + dy = dy * level; + } + else + { + dy = dy * (level < 0 ? -0.01 : 0.01); + } + + // Abs so that direction does not flip + + if (window < 0.0) + { + dx = -1 * dx; + } + if (level < 0.0) + { + dy = -1 * dy; + } + + // Compute new window level + + double newWindow = dx + window; + double newLevel = level - dy; + + if (newWindow < 0.01) + { + newWindow = 0.01; + } + + this->CurrentImageProperty->SetColorWindow(newWindow); + this->CurrentImageProperty->SetColorLevel(newLevel); + + this->Interactor->Render(); + } +} + +void ActorDraggableInteractorStyle::OnRightButtonDown() +{ + int x = this->Interactor->GetEventPosition()[0]; + int y = this->Interactor->GetEventPosition()[1]; + + this->FindPokedRenderer(x, y); + if (this->CurrentRenderer == nullptr) + { + return; + } + // Redefine this button to handle window/level + this->GrabFocus(this->EventCallbackCommand); + if (dragProp) { + dragProp->InvokeEvent(DraggableStyleEvents::RightButtonClickEvent, this->Interactor); + } +} +void ActorDraggableInteractorStyle::OnLeftButtonDown() { + int x = this->Interactor->GetEventPosition()[0]; + int y = this->Interactor->GetEventPosition()[1]; + + this->FindPokedRenderer(x, y); + if (this->CurrentRenderer == nullptr) + { + return; + } + // Redefine this button to handle window/level + this->GrabFocus(this->EventCallbackCommand); + if (selectedProp) { + selectedProp->InvokeEvent(DraggableActor::DraggableActorEvents::UnSelectedEvent); + selectedProp = nullptr; + } + if (dragProp){ + selectedProp = dragProp; + selectedProp->InvokeEvent(DraggableActor::DraggableActorEvents::SelectedEvent); + if (this->Interactor->GetRepeatCount()) + { + dragProp->InvokeEvent(DraggableStyleEvents::PopPropEvent, nullptr); + } + DragStartOrigin[0] = x; + DragStartOrigin[1] = y; + this->StartDrag(); + return; + } + if (Interactor->GetRepeatCount()){ + if (measure) + { + measure->SetPlacing(measure->onMeasureDoubleClick(this->Interactor)); + if (!measure->isMeasurePlacing()) { + this->EndMeasure(); + auto temp = measure; + measure = measure->GetNextMeasure(); + if (!temp->Valid()){ + temp->ForceDelete(); + temp= nullptr; + } + } + return; + } + this->InvokeEvent(DraggableStyleEvents::DoubleClickEvent, nullptr); + return; + } + if (measure) + { + measure->SetPlacing(measure->onMeasureLeftButtonDown(this->Interactor)); + if(this->State!=VTKIS_MEASURE) this->StartMeasure(); + return; + } + + if (scalarProp) + { + ScalarStartPosition[0] = x; + ScalarStartPosition[1] = y; + this->StartColorMapping(); + return; + } + + + if (this->InteractionMode == VTKIS_IMAGE_WINDOWLEVEL) + { + this->WindowLevelStartPosition[0] = x; + this->WindowLevelStartPosition[1] = y; + this->StartWindowLevel(); + } + else if (this->InteractionMode == VTKIS_IMAGE_ZOOM) + { + this->DollyStartScale = this->CurrentRenderer->GetActiveCamera()->GetParallelScale(); + this->StartDolly(); + } + else if (this->InteractionMode == VTKIS_IMAGE_PAN) + { + this->StartPan(); + } + else if (this->InteractionMode == VTKIS_IMAGE_SLICING) + { + this->StartSlice(); + } + +} + +void ActorDraggableInteractorStyle::ActiveMeasure(Measure *m) { + if (this->measure && nullptr == m) { + this->measure->onTerminate(this->Interactor); + } + this->measure = m; +} + +void ActorDraggableInteractorStyle::UnActiveMeasure() { + if (this->measure) { + this->measure->onTerminate(this->Interactor); + } + this->EndMeasure(); + this->measure = nullptr; +} + + +void ActorDraggableInteractorStyle::DispatchEvent() { + switch (this->State) { + case VTKIS_SLICE: + + if (this->HandleObservers) { + //double check + if (!this->CurrentImageSlice) { + this->SetCurrentImageNumber(this->CurrentImageNumber); + } + if (!this->CurrentImageSlice) return; + vtkImageSliceMapper *mapper = vtkImageSliceMapper::SafeDownCast(this->CurrentImageSlice->GetMapper()); + int slice[1] = {mapper ? mapper->GetSliceNumber() : -1}; + //榧犳爣婊戝姩涓嶄竴瀹氳兘閫犳垚缈婚〉锛侊紒锛佹墍浠ラ渶瑕佽繘琛屼竴娆″垽瀹 + if (slice[0] != lastslice) { + this->InvokeEvent(DraggableStyleEvents::SlicedEvent, slice); + lastslice = slice[0]; + } + break; + } + } +} + +//閲嶅啓閮ㄥ垎閫昏緫锛屽湪鍙杋mageProperty鐨勬椂鍊欐妸ImageSlice涔熷彇浜嗐 +void ActorDraggableInteractorStyle::SetCurrentImageNumber(int i) +{ + this->CurrentImageNumber = i; + if (!this->CurrentRenderer) + { + return; + } + vtkPropCollection* props = this->CurrentRenderer->GetViewProps(); + vtkProp* prop = nullptr; + vtkAssemblyPath* path; + vtkImageSlice* imageProp = nullptr; + vtkCollectionSimpleIterator pit; + + for (int k = 0; k < 2; k++) + { + int j = 0; + for (props->InitTraversal(pit); (prop = props->GetNextProp(pit));) + { + bool foundImageProp = false; + for (prop->InitPathTraversal(); (path = prop->GetNextPath());) + { + vtkProp* tryProp = path->GetLastNode()->GetViewProp(); + imageProp = vtkImageSlice::SafeDownCast(tryProp); + if (imageProp) + { + if (j == i && imageProp->GetPickable()) + { + foundImageProp = true; + break; + } + imageProp = nullptr; + j++; + } + } + if (foundImageProp) + { + break; + } + } + if (i < 0) + { + i += j; + } + } + + vtkImageProperty* property = nullptr; + if (imageProp) + { + property = imageProp->GetProperty(); + if (imageProp != this->CurrentImageSlice) + { + if (this->CurrentImageSlice) + { + this->CurrentImageSlice->Delete(); + } + + this->CurrentImageSlice = imageProp; + + if (this->CurrentImageSlice) + { + this->CurrentImageSlice->Register(this); + } + } + } + + if (property != this->CurrentImageProperty) + { + if (this->CurrentImageProperty) + { + this->CurrentImageProperty->Delete(); + } + + this->CurrentImageProperty = property; + + if (this->CurrentImageProperty) + { + this->CurrentImageProperty->Register(this); + } + } +} + +void ActorDraggableInteractorStyle::EndDolly() { + if (this->State != VTKIS_DOLLY) + { + return; + } + this->StopState(); + if (this->CurrentRenderer == nullptr) + { + return; + } + //婵鍙戠缉鏀惧畬鎴愪簨浠 + if (this->HandleObservers) { + vtkCamera *camera = this->CurrentRenderer->GetActiveCamera(); + double result[2] = {0.0, 0.0}; + //image 蹇呯劧鏄疨arallelProjection + result[0] = this->DollyStartScale; + result[1] = camera->GetParallelScale(); + this->InvokeEvent(DraggableStyleEvents::EndDollyEvent, result); + } +} + +void ActorDraggableInteractorStyle::StartPan() { + vtkInteractorStyle::StartPan(); + vtkCamera* camera = this->CurrentRenderer->GetActiveCamera(); + camera->GetFocalPoint(PanStartOrigin); +} + +void ActorDraggableInteractorStyle::EndPan() { + if (this->State != VTKIS_PAN) return; + if (this->HandleObservers) { + vtkCamera *camera = this->CurrentRenderer->GetActiveCamera(); + double *nf = camera->GetFocalPoint(); + double calldata[6] = {PanStartOrigin[0], PanStartOrigin[1], PanStartOrigin[2], nf[0], nf[1], nf[2]}; + this->InvokeEvent(vtkCommand::EventIds::EndPanEvent, calldata); + } + this->StopState(); +} + +void ActorDraggableInteractorStyle::EndWindowLevel() { + if (this->State != VTKIS_WINDOW_LEVEL) { + return; + } + if (this->HandleObservers) { + double calldata[2] = {this->CurrentImageProperty->GetColorWindow(),this->CurrentImageProperty->GetColorLevel()}; + this->InvokeEvent(vtkCommand::EndWindowLevelEvent, calldata); + } + this->StopState(); +} +void ActorDraggableInteractorStyle::OnChar() { + vtkInteractorStyleImage::OnChar(); + vtkRenderWindowInteractor* rwi = this->Interactor; + std::string keySym = rwi->GetKeySym(); + if (keySym=="Delete") + { + if (selectedProp) + { + this->InvokeEvent(DraggableStyleEvents::DeleteMeasureEvent,selectedProp); + } + } +} + + diff --git a/src/src/measure/AngleAnnotationActor.cpp b/src/src/measure/AngleAnnotationActor.cpp new file mode 100644 index 0000000..6e2b4a0 --- /dev/null +++ b/src/src/measure/AngleAnnotationActor.cpp @@ -0,0 +1,217 @@ +// +// Created by on 2021/7/11. +// + +#include +#include "AngleAnnotationActor.h" +#include "vtkObjectFactory.h" +#include "vtkPolyData.h" +#include "vtkCellArray.h" +#include "vtkProperty2D.h" +#include "vtkRenderer.h" +#include "vtkRenderWindow.h" +#include "ControlPointActor.h" +#include "vtkTextMapper.h" +#include "vtkTextProperty.h" + +vtkStandardNewMacro(AngleAnnotationActor) + +void AngleAnnotationActor::SetWorldPosition1(double x, double y, double z) { + BaseDataPoints->SetPoint(0,x,y,z); +} + +void AngleAnnotationActor::SetWorldPosition2(double x, double y, double z) { + BaseDataPoints->SetPoint(1,x,y,z); +} + +void AngleAnnotationActor::SetWorldPosition3(double x, double y, double z) { + BaseDataPoints->SetPoint(2,x,y,z); +} + +void AngleAnnotationActor::BuildShape() { + if (!BaseDataPoints->GetNumberOfPoints()) return; + RebuildRenderPoint(); + double p1[3]={0.0,0.0,0.0}; + double p2[3]={0.0,0.0,0.0}; + double p3[3]={0.0,0.0,0.0}; + renderPoints->GetPoint(0,p1); + renderPoints->GetPoint(1,p2); + renderPoints->GetPoint(2,p3); + double v1[3] = {p1[0]-p2[0],p1[1]-p2[1],0.0}; + double v2[3] = {p3[0]-p2[0],p3[1]-p2[1],0.0}; + double angle = vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(v1,v2)); + char str[100]; + sprintf_s(str,"%.0f",angle); + vtkTextMapper::SafeDownCast(text->GetMapper())->SetInput(str); + text->SetDisplayPosition(p2[0]+10,p2[1]-20); + vtkNew source; + source->SetPoints(renderPoints); + source->Update(); + renderData->DeepCopy(source->GetOutput()); + if (Measure::Hidden) + { + this->Hide(); + controlP1->Hide(); + controlP2->Hide(); + controlP3->Hide(); + } + else{ + this->Show(); + controlP1->Show(); + controlP2->Show(); + controlP3->Show(); + } +} + +AngleAnnotationActor::AngleAnnotationActor() { + controlP1 = ControlPointActor::New(); + controlP2 = ControlPointActor::New(); + controlP3 = ControlPointActor::New(); + text = vtkActor2D::New(); + vtkNew textMapper; + textMapper->SetInput("0"); + text->SetMapper(textMapper); + textProperty = textMapper->GetTextProperty(); + textProperty->SetBold(true); + textProperty->SetFontFamilyToArial(); + textProperty->SetFontSize(16); + textProperty->SetColor(0.8,0.8,0.0); + textProperty->SetOpacity(0.75); + textProperty->SetFrame(true); + textProperty->SetFrameColor(1.0,0.0,0.0); + textProperty->SetBackgroundColor(1.0,0.0,0.0); + textProperty->SetBackgroundOpacity(0.3); + BaseDataPoints->SetNumberOfPoints(3); + BaseDataPoints->SetPoint(0,0,0,0); + BaseDataPoints->SetPoint(1,512,512,0); + renderPoints->SetNumberOfPoints(3); + controlP1->AddObserver(DraggableActorEvents::DragEvent, this, &AngleAnnotationActor::controlPointCb); + controlP2->AddObserver(DraggableActorEvents::DragEvent, this, &AngleAnnotationActor::controlPointCb); + controlP3->AddObserver(DraggableActorEvents::DragEvent, this, &AngleAnnotationActor::controlPointCb); + this->AddObserver(DraggableActorEvents::DragEvent, this, &AngleAnnotationActor::selfDragCb); +} + +AngleAnnotationActor::~AngleAnnotationActor() { + controlP1->Delete(); + controlP1 = nullptr; + controlP2->Delete(); + controlP2 = nullptr; + controlP3->Delete(); + controlP3 = nullptr; + text->Delete(); + text = nullptr; + +} + +void AngleAnnotationActor::Highlight(int highlightOn) { + if (this->Highlighted){ + actor2D->GetProperty()->SetColor(1.0, 1.0,0.0); + textProperty->SetColor(1.0,1.0,0.0); + return; + } + if (this->Selected){ + actor2D->GetProperty()->SetColor(1.0, 0.0,0.0); + textProperty->SetColor(1.0,0.0,0.0); + return; + } + actor2D->GetProperty()->SetColor(0.0, 1.0,0.0); + textProperty->SetColor(0.0,1.0,0.0); + this->Renderer->GetRenderWindow()->Render(); +} + +void AngleAnnotationActor::SetRenderer(vtkRenderer *ren) { + DraggableActor::SetRenderer(ren); + controlP1->SetRenderer(ren); + controlP2->SetRenderer(ren); + controlP3->SetRenderer(ren); + +} + +void AngleAnnotationActor::onMeasureMouseMove(vtkRenderWindowInteractor *iren) { + int x = iren->GetEventPosition()[0]; + int y = iren->GetEventPosition()[1]; + vtkRenderer *renderer = iren->FindPokedRenderer(x, y); + if (!renderer) return; + renderer->SetDisplayPoint(x, y, 0.0); + renderer->DisplayToWorld(); + double *p = renderer->GetWorldPoint(); + if (this->placedPointCount==1) + { + controlP2->SetWorldPosition(p); + this->SetWorldPosition2(p); + } + controlP3->SetWorldPosition(p); + this->SetWorldPosition3(p); + iren->Render(); +} + +bool AngleAnnotationActor::onMeasureLeftButtonDown(vtkRenderWindowInteractor *iren) { + int x = iren->GetEventPosition()[0]; + int y = iren->GetEventPosition()[1]; + vtkRenderer *renderer = iren->FindPokedRenderer(x, y); + if (!renderer) return false; + renderer->SetDisplayPoint(x, y, 0.0); + renderer->DisplayToWorld(); + double *p = renderer->GetWorldPoint(); + if (this->placedPointCount==0) + { + controlP1->SetWorldPosition(p); + controlP2->SetWorldPosition(p); + controlP3->SetWorldPosition(p); + this->SetWorldPosition1(p); + this->SetWorldPosition2(p); + this->SetWorldPosition3(p); + this->SetRenderer(renderer); + controlP1->Highlight(0); + this->placedPointCount=1; + } + this->SetWorldPosition3(p); + if (this->placedPointCount==2){ + this->placedPointCount=3; + controlP2->Highlight(0); + } + iren->Render(); + return this->placedPointCount<3; +} + +bool AngleAnnotationActor::onMeasureLeftButtonUp(vtkRenderWindowInteractor *iren) { + int x = iren->GetEventPosition()[0]; + int y = iren->GetEventPosition()[1]; + vtkRenderer *renderer = iren->FindPokedRenderer(x, y); + if (!renderer) return true; + renderer->SetDisplayPoint(x, y, 0.0); + renderer->DisplayToWorld(); + double *p = renderer->GetWorldPoint(); + if (this->placedPointCount==1){ + controlP2->SetWorldPosition(p); + this->SetWorldPosition2(p); + controlP3->SetWorldPosition(p); + this->SetWorldPosition3(p); + this->placedPointCount++; + return true; + } + return false; +} + +void AngleAnnotationActor::controlPointCb(vtkObject *sender, unsigned long event, void *data) { + int index = sender == controlP1 ? 0 : (sender == controlP2?1:2); + vtkPoints *pts = static_cast(data); + double *pos = pts->GetPoint(0); + double result[3] = {0, 0, 0}; + GetSlicePlanePoint(pos[0], pos[1], this->Renderer, result); + BaseDataPoints->SetPoint(index, result); +} + +void AngleAnnotationActor::selfDragCb(vtkObject *, unsigned long, void *data) { + vtkPoints *pts = static_cast(data); + double *pos = pts->GetPoint(0); + double result[3] = {0, 0, 0}; + GetSlicePlanePoint(pos[0], pos[1], this->Renderer, result); + controlP1->SetWorldPosition(result); + pos = pts->GetPoint(1); + GetSlicePlanePoint(pos[0], pos[1], this->Renderer, result); + controlP2->SetWorldPosition(result); + pos = pts->GetPoint(2); + GetSlicePlanePoint(pos[0], pos[1], this->Renderer, result); + controlP3->SetWorldPosition(result); +} \ No newline at end of file diff --git a/src/src/measure/AnnotationActor.cpp b/src/src/measure/AnnotationActor.cpp new file mode 100644 index 0000000..d634395 --- /dev/null +++ b/src/src/measure/AnnotationActor.cpp @@ -0,0 +1,237 @@ +// +// Created by 87714 on 2021/6/7. +// + +#include "AnnotationActor.h" +#include "vtkPolyData.h" +#include "vtkPolyLineSource.h" +#include "vtkObjectFactory.h" +#include "vtkCallbackCommand.h" +#include "vtkCommand.h" +#include "vtkProperty2D.h" +#include "vtkRenderer.h" +#include "vtkRenderWindow.h" +#include "vtkRenderWindowInteractor.h" +#include "vtkAssemblyPaths.h" +#include "vtkAssemblyPath.h" +#include "vtkInformation.h" +#include "vtkTransform.h" +#include "vtkTransformFilter.h" +#include "vtkTransform.h" +#include "vtkPolyData.h" +#include "vtkParametricSpline.h" +#include "vtkParametricFunctionSource.h" +#include "vtkCursor2D.h" + +#include "vtkCamera.h" + +//鏆傛椂鍙В鍐虫浜ゅ潗鏍囩郴闂锛侊紒锛 +void GetSlicePlanePoint(double x,double y,vtkRenderer * renderer, double* result) +{ + vtkCamera* camera = renderer->GetActiveCamera(); + double* fp = camera->GetFocalPoint(); + double* dp = camera->GetDirectionOfProjection(); + renderer->SetDisplayPoint(x, y, 0); + renderer->DisplayToWorld(); + double* p = renderer->GetWorldPoint(); + result[0] = dp[0] > 0.0 ? fp[0] : p[0]; + result[1] = dp[1] > 0.0 ? fp[1] : p[1]; + result[2] = dp[2] >0.0 ? fp[2] : p[2]; +} + + + + + +vtkStandardNewMacro(AnnotationActor); + +AnnotationActor::AnnotationActor() { + renderer= nullptr; + BaseDataPoints= vtkPoints::New(); + renderPoints= vtkPoints::New(); + BaseDataPoints->SetNumberOfPoints(3); + BaseDataPoints->SetPoint(0,0,0,0); + BaseDataPoints->SetPoint(1,50,50,0); + BaseDataPoints->SetPoint(2,100,150,0); +#ifdef _DEBUG + printf("========================= %d\r\n", int(renderPoints->GetNumberOfPoints())); +#endif + BuildAnnotation(); + mapper = vtkPolyDataMapper2D::New(); + mapper->SetInputData(renderData); + actor2D = vtkActor2D::New(); + actor2D->SetMapper(mapper); + mapper2 = vtkPolyDataMapper2D::New(); + actor2D2 = vtkActor2D::New(); + actor2D2->SetMapper(mapper2); + mapper2->SetInputConnection(cursor->GetOutputPort()); +} + +AnnotationActor::~AnnotationActor() { + +} + +void AnnotationActor::ReleaseGraphicsResources(vtkWindow * window) { + // mapper->SetInputData(renderData); + actor2D->ReleaseGraphicsResources(window); + vtkProp::ReleaseGraphicsResources( window); +} + +int AnnotationActor::RenderOverlay(vtkViewport *viewport) { + + BuildAnnotation(); + actor2D->RenderOverlay(viewport); + actor2D2->RenderOverlay(viewport); + return vtkProp::RenderOverlay(viewport); +} + +vtkRenderer *AnnotationActor::GetRenderer() { + return renderer; +} + +void AnnotationActor::SetRenderer(vtkRenderer *ren) { + if (ren) + { + if (ren==renderer) return; + if (renderer) renderer->RemoveViewProp(this); + renderer= ren; + renderer->AddViewProp(this); +// renderer->GetRenderWindow()->GetInteractor()->AddObserver(vtkCommand::EventIds::EnterEvent,this,&AnnotationActor::printPick2); + } else{ + if (renderer) renderer->RemoveViewProp(this); + renderer = nullptr; + } +} + +void AnnotationActor::SetDisplayPosition(int index, double *pos) { + +} + +double *AnnotationActor::GetWorldPosition(int index) { + return nullptr; +} + +double *AnnotationActor::GetDisplayPosition(int index) { + return nullptr; +} + +void AnnotationActor::GetDisplayPosition(int index, double *pos) { + +} + +void AnnotationActor::SetWorldPosition(int index, double *pos) { + +} + +void AnnotationActor::GetWorldPosition(int index, double *pos) { + +} + +void AnnotationActor::PrintSelf(ostream &os, vtkIndent indent) { + vtkProp::PrintSelf(os, indent); +} + +void AnnotationActor::GetActors2D(vtkPropCollection * collection) { + vtkProp::GetActors2D( collection); +} + +void AnnotationActor::Pick() { +// this->GetRenderer()->GetRenderWindow()->GetInteractor()->GetEventPositions(); + if (this->GetRenderer()){ + int * p = this->GetRenderer()->GetRenderWindow()->GetInteractor()->GetEventPosition(); + double pos[2]; + pos[0] = (double)p[0]; + pos[1] = (double)p[1]; + double* bounds = mapper->GetInput()->GetBounds(); + if (bounds[0]<=pos[0] && bounds[1]>=pos[0] && bounds[2]<=pos[1] && bounds[1]>=pos[1]) { + actor2D->Pick(); + return; + } + } + vtkProp::Pick(); +} + +void AnnotationActor::transform(float x, float y) { + if (!tempStorePoints) { + tempStorePoints = vtkPoints::New(); + tempStorePoints->DeepCopy(renderPoints); + transforming = true; + } + vtkTransform* trans = vtkTransform::New(); + trans->Translate(x,y,0); + vtkNew filter; + filter->SetTransform(trans); + vtkNew poly; + poly->SetPoints(tempStorePoints); + filter->SetInputData(poly); + filter->Update(); + renderPoints->DeepCopy(filter->GetPolyDataOutput()->GetPoints()); +} + +void AnnotationActor::ApplyTransform() { + if (tempStorePoints) + { + tempStorePoints->Delete(); + tempStorePoints= nullptr; + transforming = false; + } + //鍙嶆帹3d鐨凱oint + BaseDataPoints->Reset(); + for (int i =0; i< renderPoints->GetNumberOfPoints(); i++) + { + double* pos = renderPoints->GetPoint(i); + double wpos[3] = {0,0,0}; + GetSlicePlanePoint(pos[0], pos[1], this->renderer, wpos); + BaseDataPoints->InsertNextPoint(wpos); + } + +} + +void AnnotationActor::Highlight(int highlightOn) { + if (highlightOn>0)this->actor2D->GetProperty()->SetColor(255,0,0); + else this->actor2D->GetProperty()->SetColor(255,255,255); + renderer->GetRenderWindow()->Render(); +} + +void AnnotationActor::BuildAnnotation() { + if (!renderer) return; + if (!transforming){ + renderPoints->Reset(); + for (int i=0; i< BaseDataPoints->GetNumberOfPoints(); i++) + { + + renderer->SetWorldPoint(BaseDataPoints->GetPoint(i)); + renderer->WorldToDisplay(); + double* p = renderer->GetDisplayPoint(); +#ifdef _DEBUG + printf("%f, %f, %f;\r\n", p[0],p[1],p[2]); +#endif + renderPoints->InsertNextPoint(p[0],p[1],0); + } + } +// vtkNew spline; + //printf("%lld;\r\n", renderPoints->GetNumberOfPoints()); +// +// spline->SetPoints(renderPoints); +// vtkNew source; +// source->SetParametricFunction(spline); +// source->Update(); + + cursor->SetRadius(5); + double* p1 = BaseDataPoints->GetPoint(0); + cursor->SetFocalPoint(p1[0], p1[1], p1[2]); + cursor->Modified(); + + + vtkNew source; +#ifdef _DEBUG + printf("%d\r\n", int(renderPoints->GetNumberOfPoints())); +#endif + source->SetPoints(renderPoints); + source->Update(); +// path- + renderData->DeepCopy(source->GetOutput()); + // mapper->SetInputData(renderData); + // renderData->Modified(); +} + diff --git a/src/src/measure/ArrowAnnotationActor.cpp b/src/src/measure/ArrowAnnotationActor.cpp new file mode 100644 index 0000000..293f167 --- /dev/null +++ b/src/src/measure/ArrowAnnotationActor.cpp @@ -0,0 +1,99 @@ +// +// Created by 87714 on 2021/8/10. +// + +#include "ArrowAnnotationActor.h" +#include "vtkObjectFactory.h" +#include "vtkPolyData.h" +#include "vtkCellArray.h" +#include "vtkProperty2D.h" +#include "vtkRenderer.h" +#include "vtkRenderWindow.h" +#include "ControlPointRActor.h" +#include + + + +vtkStandardNewMacro(ArrowAnnotationActor) + +void ArrowAnnotationActor::BuildShape() { + if (!BaseDataPoints->GetNumberOfPoints()) return; + RebuildRenderPoint(); + double p1[3] = {0.0, 0.0, 0.0}; + double p2[3] = {0.0, 0.0, 0.0}; + renderPoints->GetPoint(0,p1); + renderPoints->GetPoint(1,p2); + double d =sqrt(vtkMath::Distance2BetweenPoints(p1,p2)); + double kx = (p2[0]-p1[0])/d; + double ky = (p2[1]-p1[1])/d; + arrowRenderPoints->SetPoint(0,p1[0]+5.0*kx,p1[1]+5.0*ky,0.0); + arrowRenderPoints->SetPoint(1,p2[0]-5.0*kx,p2[1]-5.0*ky,0.0); + vtkNew source; + source->SetPoints(arrowRenderPoints); + source->Update(); + renderData->DeepCopy(source->GetOutput()); + if (Measure::Hidden) + { + this->Hide(); + controlP1->Hide(); + controlP2->Hide(); + } + else{ + this->Show(); + controlP1->Show(); + controlP2->Show(); + } +} + +ArrowAnnotationActor::ArrowAnnotationActor() { + actor2D->GetProperty()->SetLineWidth(3.0); + shadow2D->GetProperty()->SetLineWidth(5.0); + controlP1 = ControlPointRActor::New(); + controlP2 = ControlPointRActor::New(); + BaseDataPoints->SetNumberOfPoints(2); + BaseDataPoints->SetPoint(0, 0, 0, 0); + BaseDataPoints->SetPoint(1, 512, 512, 0); + controlP1->SetWorldPosition(0, 0, 0); + controlP2->SetWorldPosition(512, 512, 0); + renderPoints->SetNumberOfPoints(2); + arrowRenderPoints = vtkPoints::New(); + arrowRenderPoints->SetNumberOfPoints(2); + controlP1->AddObserver(DraggableActorEvents::DragEvent, this, &ArrowAnnotationActor::controlPointCb); + controlP1->AddObserver(vtkCommand::EnterEvent, this, &ArrowAnnotationActor::HighlightOn); + controlP1->AddObserver(vtkCommand::LeaveEvent, this, &ArrowAnnotationActor::HighlightOff); + controlP2->AddObserver(DraggableActorEvents::DragEvent, this, &ArrowAnnotationActor::controlPointCb); + controlP2->AddObserver(vtkCommand::EnterEvent, this, &ArrowAnnotationActor::HighlightOn); + controlP2->AddObserver(vtkCommand::LeaveEvent, this, &ArrowAnnotationActor::HighlightOff); + this->AddObserver(DraggableActorEvents::DragEvent, this, &ArrowAnnotationActor::selfDragCb); +} + +ArrowAnnotationActor::~ArrowAnnotationActor() { + arrowRenderPoints->Delete(); + arrowRenderPoints = nullptr; +} + +void ArrowAnnotationActor::Highlight(int highlightOn) { + controlP1->Highlight(highlightOn); + controlP2->Highlight(highlightOn); + DraggableActor::Highlight(highlightOn); +} + +bool ArrowAnnotationActor::onMeasureLeftButtonDown(vtkRenderWindowInteractor *iren) { + int x = iren->GetEventPosition()[0]; + int y = iren->GetEventPosition()[1]; + vtkRenderer *renderer = iren->FindPokedRenderer(x, y); + if (!renderer) return false; + renderer->SetDisplayPoint(x, y, 0.0); + renderer->DisplayToWorld(); + double *p = renderer->GetWorldPoint(); + controlP1->SetWorldPosition(p); + controlP1->Highlight(1); + controlP2->SetWorldPosition(p); + this->SetWorldPosition1(p); + this->SetWorldPosition2(p); + this->SetRenderer(renderer); + iren->Render(); + return true; +} + + diff --git a/src/src/measure/ControlPointActor.cpp b/src/src/measure/ControlPointActor.cpp new file mode 100644 index 0000000..37c2894 --- /dev/null +++ b/src/src/measure/ControlPointActor.cpp @@ -0,0 +1,73 @@ +// +// Created by 87714 on 2021/6/22. +// + +#include +#include "ControlPointActor.h" +#include "vtkObjectFactory.h" +#include "vtkPolyData.h" +#include "vtkCellArray.h" +#include "vtkProperty2D.h" +#include "vtkRenderer.h" +#include "vtkRenderWindow.h" + +vtkStandardNewMacro(ControlPointActor) + +ControlPointActor::ControlPointActor() { + BaseDataPoints->SetNumberOfPoints(1); + renderPoints->SetNumberOfPoints(1); + renderPoints->SetPoint(0, 0, 0, 0); + actor2D->GetProperty()->SetColor(1.0,0,0); + vtkPolyDataMapper2D* mapper = vtkPolyDataMapper2D::SafeDownCast(shadow2D->GetMapper()); + mapper->SetInputData(shadowData); +} + +ControlPointActor::~ControlPointActor() { + +} + +void ControlPointActor::BuildShape() { + if (!BaseDataPoints->GetNumberOfPoints()) return; + RebuildRenderPoint(); + double * p = renderPoints->GetPoint(0); + vtkNew pts; + vtkNew lines; + renderData->SetPoints(pts); + vtkIdType pids[2]; + pids[0] = pts->InsertNextPoint(p[0]+4.0,p[1]+4.0,0); + pids[1] =pts->InsertNextPoint(p[0]-4.0,p[1]-4.0,0); + lines->InsertNextCell(2,pids); + pids[0] = pts->InsertNextPoint(p[0]-4.0,p[1]+4.0,0); + pids[1] = pts->InsertNextPoint(p[0]+4.0,p[1]-4.0,0); + lines->InsertNextCell(2,pids); + renderData->SetLines(lines); + vtkNew pts2; + vtkNew lines2; + shadowData->SetPoints(pts2); + vtkIdType pids2[2]; + pids2[0] = pts2->InsertNextPoint(p[0]+4.5,p[1]+4.5,0); + pids2[1] = pts2->InsertNextPoint(p[0]-4.5,p[1]-4.5,0); + lines2->InsertNextCell(2,pids2); + pids2[0] = pts2->InsertNextPoint(p[0]-4.5,p[1]+4.5,0); + pids2[1] = pts2->InsertNextPoint(p[0]+4.5,p[1]-4.5,0); + lines2->InsertNextCell(2,pids2); + shadowData->SetLines(lines2); +} +void ControlPointActor::Highlight(int highlightOn) { + if (highlightOn > 0) { + actor2D->GetProperty()->SetColor(1.0, 1.0, 0); + } else { + actor2D->GetProperty()->SetColor(1.0, 0.0, 0); + } + if (this->Renderer)this->Renderer->GetRenderWindow()->Render(); +} + +void ControlPointActor::SetWorldPosition(double x, double y, double z) { + BaseDataPoints->Reset(); + BaseDataPoints->SetNumberOfPoints(1); + BaseDataPoints->SetPoint(0,x,y,z); +}; + +double* ControlPointActor::GetWorldPosition() { + return BaseDataPoints->GetPoint(0); +}; \ No newline at end of file diff --git a/src/src/measure/ControlPointRActor.cpp b/src/src/measure/ControlPointRActor.cpp new file mode 100644 index 0000000..d293972 --- /dev/null +++ b/src/src/measure/ControlPointRActor.cpp @@ -0,0 +1,49 @@ +// +// Created by 87714 on 2021/8/10. +// + +#include +#include "ControlPointRActor.h" +#include "vtkObjectFactory.h" +#include "vtkPolyData.h" +#include "vtkCellArray.h" +#include "vtkProperty2D.h" +#include "vtkRenderer.h" +#include "vtkRenderWindow.h" +#include "vtkDiskSource.h" + +vtkStandardNewMacro(ControlPointRActor) + + +void ControlPointRActor::BuildShape() { + if (!BaseDataPoints->GetNumberOfPoints()) return; + RebuildRenderPoint(); + vtkNew disk; + disk->SetInnerRadius(0); + disk->SetOuterRadius(4); + disk->SetCircumferentialResolution(30); + double * p = renderPoints->GetPoint(0); + vtkNew diskShadow; + diskShadow->SetInnerRadius(0); + diskShadow->SetOuterRadius(5); + diskShadow->SetCircumferentialResolution(30); + disk->Update(); + diskShadow->Update(); + shadowData->DeepCopy(diskShadow->GetOutput()); + renderData->DeepCopy(disk->GetOutput()); + actor2D->SetDisplayPosition(p[0],p[1]); + shadow2D->SetDisplayPosition(p[0],p[1]); + senseArea->SetDisplayPosition(p[0],p[1]); +} +void ControlPointRActor::Highlight(int highlightOn) { + if (highlightOn > 0) { + actor2D->GetProperty()->SetColor(1.0, 1.0, 0); + actor2D->GetProperty()->SetOpacity(1.0); + shadow2D->GetProperty()->SetOpacity(1.0); + } else { + actor2D->GetProperty()->SetOpacity(0.0); + shadow2D->GetProperty()->SetOpacity(0.0); + actor2D->GetProperty()->SetColor(1.0, 0.0, 0); + } + if (this->Renderer)this->Renderer->GetRenderWindow()->Render(); +} diff --git a/src/src/measure/DraggableActor.cpp b/src/src/measure/DraggableActor.cpp new file mode 100644 index 0000000..40acdc1 --- /dev/null +++ b/src/src/measure/DraggableActor.cpp @@ -0,0 +1,244 @@ +// +// Created by 87714 on 2021/6/21. +// + +#include +#include +#include +#include "DraggableActor.h" +#include "vtkObjectFactory.h" +#include "vtkRenderer.h" +#include "vtkRenderWindow.h" +#include "vtkCamera.h" +#include "vtkCommand.h" +#include "vtkProperty2D.h" + + + + +//鏆傛椂鍙В鍐虫浜ゅ潗鏍囩郴闂锛侊紒锛 +void DraggableActor::GetSlicePlanePoint(double x,double y,vtkRenderer * renderer, double* result) +{ + vtkCamera* camera = renderer->GetActiveCamera(); + double* fp = camera->GetFocalPoint(); + double* dp = camera->GetDirectionOfProjection(); + renderer->SetDisplayPoint(x, y, 0); + renderer->DisplayToWorld(); + double* p = renderer->GetWorldPoint(); + result[0] = dp[0] > 0.0 ? fp[0] : p[0]; + result[1] = dp[1] > 0.0 ? fp[1] : p[1]; + result[2] = dp[2] >0.0 ? fp[2] : p[2]; +} + +vtkStandardNewMacro(DraggableActor); + +void DraggableActor::Transform(float x, float y) { + if (!tempStorePoints) { + tempStorePoints = vtkPoints::New(); + tempStorePoints->DeepCopy(renderPoints); + transforming = true; + } + vtkNew trans; + trans->Translate(x,y,0); + vtkNew filter; + filter->SetTransform(trans); + vtkNew poly; + poly->SetPoints(tempStorePoints); + filter->SetInputData(poly); + filter->Update(); + renderPoints->DeepCopy(filter->GetPolyDataOutput()->GetPoints()); + //vtk瀵硅薄鍐呴儴浣跨敤 + this->InvokeEvent(DraggableActorEvents::DragEvent,renderPoints); + if (this->OnDrag){ + OnDrag(renderPoints); + } +} + +void DraggableActor::ApplyTransform() { + if (tempStorePoints) + { + tempStorePoints->Delete(); + tempStorePoints= nullptr; + transforming = false; + } + //鍙嶆帹3d鐨凱oint + BaseDataPoints->Reset(); + for (int i =0; i< renderPoints->GetNumberOfPoints(); i++) + { + double* pos = renderPoints->GetPoint(i); + double wpos[3] = {0,0,0}; + GetSlicePlanePoint(pos[0], pos[1], this->Renderer, wpos); + BaseDataPoints->InsertNextPoint(wpos); + } + //vtk瀵硅薄鍐呴儴浣跨敤 + this->InvokeEvent(DraggableActorEvents::DragEndEvent, BaseDataPoints); + //澶栭儴浣跨敤锛屽彧鑳借缃竴涓 + if (this->OnDragEnd){ + OnDragEnd(BaseDataPoints); + } +} + +DraggableActor::DraggableActor() { + //if (Renderer) Renderer->RemoveViewProp(this); + Renderer= nullptr; + BaseDataPoints = vtkPoints::New(); + renderPoints= vtkPoints::New(); + vtkNew mapper; + mapper->SetInputData(renderData); + actor2D = vtkActor2D::New(); + actor2D->SetMapper(mapper); + vtkNew smapper; + smapper->SetInputData(renderData); + shadow2D = vtkActor2D::New(); + shadow2D->SetMapper(smapper); + vtkNew ssmapper; + ssmapper->SetInputData(renderData); + senseArea = vtkActor2D::New(); + senseArea->SetMapper(ssmapper); + actor2D->GetProperty()->SetColor(0.0, 1.0, 0); + actor2D->GetProperty()->SetLineWidth(1.0); + shadow2D->GetProperty()->SetColor(0.0, 0.0, 0.0); + shadow2D->GetProperty()->SetLineWidth(3.0); + senseArea->GetProperty()->SetColor(0.0, 0.0, 0.0); + senseArea->GetProperty()->SetLineWidth(16.0); + senseArea->GetProperty()->SetOpacity(0.0); + this->AddObserver(DraggableActorEvents::SelectedEvent,this,&DraggableActor::SelectedOn); + this->AddObserver(DraggableActorEvents::UnSelectedEvent,this,&DraggableActor::SelectedOff); +} + +DraggableActor::~DraggableActor() { + if (Renderer) this->SetRenderer(nullptr); + vtkPolyDataMapper2D::SafeDownCast(actor2D->GetMapper())->SetInputData(nullptr); + actor2D->Delete(); + actor2D= nullptr; + vtkPolyDataMapper2D::SafeDownCast(shadow2D->GetMapper())->SetInputData(nullptr); + shadow2D->Delete(); + shadow2D= nullptr; + vtkPolyDataMapper2D::SafeDownCast(senseArea->GetMapper())->SetInputData(nullptr); + senseArea->Delete(); + senseArea= nullptr; + if (BaseDataPoints){ + BaseDataPoints->Delete(); + BaseDataPoints= nullptr; + } + if (renderPoints){ + renderPoints->Delete(); + renderPoints= nullptr; + } + if (tempStorePoints){ + tempStorePoints->Delete(); + tempStorePoints= nullptr; + } +} + +void DraggableActor::ReleaseGraphicsResources(vtkWindow * window) { + senseArea->ReleaseGraphicsResources(window); + shadow2D->ReleaseGraphicsResources(window); + actor2D->ReleaseGraphicsResources(window); + if (text) text->ReleaseGraphicsResources(window); + vtkProp::ReleaseGraphicsResources( window); +} + +int DraggableActor::RenderOverlay(vtkViewport *viewport) { + BuildShape(); + if(senseArea->GetVisibility())senseArea->RenderOverlay(viewport); + if(shadow2D->GetVisibility())shadow2D->RenderOverlay(viewport); + if(actor2D->GetVisibility())actor2D->RenderOverlay(viewport); + if (text && actor2D->GetVisibility()) text->RenderOverlay(viewport); + return vtkProp::RenderOverlay(viewport); +} + +void DraggableActor::SetRenderer(vtkRenderer *ren) { + if (ren) + { + if (ren==Renderer) return; + if (Renderer) Renderer->RemoveViewProp(this); + Renderer= ren; + Renderer->AddViewProp(this); + } else{ + if (Renderer) Renderer->RemoveViewProp(this); + Renderer = nullptr; + } +} + +void DraggableActor::Pick() { + actor2D->Pick(); + vtkProp::Pick(); +} + +void DraggableActor::Highlight(int highlightOn) { + if (highlightOn > 0) { + this->Highlighted = true; + } else { + this->Highlighted = false; + } + RenderWithState(); + if (this->Renderer)this->Renderer->GetRenderWindow()->Render(); +} + +void DraggableActor::Select(int Selected) { + if (Selected > 0) { + this->Selected = true; + this->Highlighted = false; + } else { + this->Selected = false; + } + RenderWithState(); + if (this->Renderer)this->Renderer->GetRenderWindow()->Render(); +} + +void DraggableActor::RenderWithState() { + if (this->Highlighted){ + actor2D->GetProperty()->SetColor(1.0, 1.0,0.0); + return; + } + if (this->Selected){ + actor2D->GetProperty()->SetColor(1.0, 0.0,0.0); + return; + } + actor2D->GetProperty()->SetColor(0.0, 1.0,0.0); +} + + +bool DraggableActor::Hit(int x, int y) { + double* bounds = renderPoints->GetBounds(); + return (x<=std::max(bounds[0],bounds[1]) && x>=std::min(bounds[0],bounds[1]) && + y<= std::max(bounds[2],bounds[3]) && y>= std::min(bounds[2],bounds[3])); +} + +void DraggableActor::RebuildRenderPoint() { + if (!transforming){ + renderPoints->Reset(); + for (int i=0; i< BaseDataPoints->GetNumberOfPoints(); i++) + { + Renderer->SetWorldPoint(BaseDataPoints->GetPoint(i)); + Renderer->WorldToDisplay(); + double* p = Renderer->GetDisplayPoint(); + renderPoints->InsertNextPoint(p[0],p[1],0); + } + } + +} + +void DraggableActor::Hide(){ + if (this->GetPickable()) { + this->SetPickable(false); + this->actor2D->SetVisibility(false); + this->shadow2D->SetVisibility(false); + this->senseArea->SetVisibility(false); + } +} + +void DraggableActor::Show() { + if (!this->GetPickable()) { + this->SetPickable(true); + this->actor2D->SetVisibility(true); + this->shadow2D->SetVisibility(true); + this->senseArea->SetVisibility(true); + } +} + + + + + diff --git a/src/src/measure/EllipseAnnotationActor.cpp b/src/src/measure/EllipseAnnotationActor.cpp new file mode 100644 index 0000000..34047a1 --- /dev/null +++ b/src/src/measure/EllipseAnnotationActor.cpp @@ -0,0 +1,320 @@ +// +// Created by 87714 on 2021/6/22. +// + +#include +#include "EllipseAnnotationActor.h" +#include "vtkObjectFactory.h" +#include "vtkPolyData.h" +#include "vtkCellArray.h" +#include "vtkProperty2D.h" +#include "vtkRenderer.h" +#include "vtkRenderWindow.h" +#include "ControlPointActor.h" +#include "vtkTextProperty.h" +#include "vtkTextMapper.h" + +#define _USE_MATH_DEFINES +#include +#include +#include +vtkStandardNewMacro(EllipseAnnotationActor) + + +const int Granularity = 30; +void EllipseAnnotationActor::BuildShape() { + if (!BaseDataPoints->GetNumberOfPoints()) return; + RebuildRenderPoint(); + + double *p_lt = controlP_lt->GetWorldPosition(); + double *p_rb = controlP_rb->GetWorldPosition(); + double area = abs((p_lt[0] - p_rb[0])*(p_lt[1] - p_rb[1]))*M_PI*0.25; + QString num = QString::number(area, 'f', 2); + QString str = QString("Area = %1 cm2").arg(num); + double * rp = renderPoints->GetPoint(0); + vtkTextMapper::SafeDownCast(text->GetMapper())->SetInput(str.toStdString().c_str()); + text->SetDisplayPosition(rp[0] + 10, rp[1] - 20); + + + + vtkNew source; + source->SetPoints(renderPoints); + source->Update(); + + renderData->DeepCopy(source->GetOutput()); + if (Measure::Hidden) + { + this->Hide(); + controlP_rt->Hide(); + controlP_lb->Hide(); + controlP_rb->Hide(); + controlP_lt->Hide(); + + } + else { + this->Show(); + controlP_rt->Show(); + controlP_lb->Show(); + controlP_rb->Show(); + controlP_lt->Show(); + } +} + +EllipseAnnotationActor::EllipseAnnotationActor() { + controlP_rt = ControlPointActor::New(); + controlP_lb = ControlPointActor::New(); + controlP_rb = ControlPointActor::New(); + controlP_lt = ControlPointActor::New(); + + controlP_lt->SetWorldPosition(0, 0, 0); + controlP_rt->SetWorldPosition(512, 0, 0); + controlP_lb->SetWorldPosition(0, 512, 0); + controlP_rb->SetWorldPosition(512, 512, 0); + + BaseDataPoints->SetNumberOfPoints(Granularity+1); + //BaseDataPoints->SetPoint(0, 0, 0, 0); + //BaseDataPoints->SetPoint(1, 512, 512, 0); + + + renderPoints->SetNumberOfPoints(Granularity+1); + controlP_lt->AddObserver(DraggableActorEvents::DragEvent, this, &EllipseAnnotationActor::controlPointCb); + controlP_rt->AddObserver(DraggableActorEvents::DragEvent, this, &EllipseAnnotationActor::controlPointCb); + controlP_lb->AddObserver(DraggableActorEvents::DragEvent, this, &EllipseAnnotationActor::controlPointCb); + controlP_rb->AddObserver(DraggableActorEvents::DragEvent, this, &EllipseAnnotationActor::controlPointCb); + + //this->AddObserver(DraggableActorEvents::DragEvent, this, &EllipseAnnotationActor::selfDragCb); + + + text = vtkActor2D::New(); + vtkNew textMapper; + textMapper->SetInput("0"); + text->SetMapper(textMapper); + textProperty = textMapper->GetTextProperty(); + textProperty->SetBold(true); + textProperty->SetFontFamilyToArial(); + textProperty->SetFontSize(16); + textProperty->SetColor(0.8, 0.8, 0.0); + textProperty->SetOpacity(0.75); + textProperty->SetFrame(false); + //textProperty->SetFrameColor(1.0,0.0,0.0); + textProperty->SetBackgroundColor(1.0, 0.0, 0.0); + textProperty->SetBackgroundOpacity(0.3); +} + +EllipseAnnotationActor::~EllipseAnnotationActor() { + controlP_lt->Delete(); + controlP_rt->Delete(); + controlP_lb->Delete(); + controlP_rb->Delete(); + + controlP_lt = nullptr; + controlP_rt = nullptr; + controlP_lb = nullptr; + controlP_rb = nullptr; + + +} + +void EllipseAnnotationActor::SetRenderer(vtkRenderer *ren) { + DraggableActor::SetRenderer(ren); + controlP_lt->SetRenderer(ren); + controlP_rt->SetRenderer(ren); + controlP_lb->SetRenderer(ren); + controlP_rb->SetRenderer(ren); +} + +void EllipseAnnotationActor::controlPointCb(vtkObject *sender, unsigned long event, void *data) { + + + vtkPoints *pts = static_cast(data); + double *pos = pts->GetPoint(0); + + double result[3] = { 0, 0, 0 }; + GetSlicePlanePoint(pos[0], pos[1], this->Renderer, result); + + //BaseDataPoints->SetPoint(index, result); + + + double *p1; + double *p2; + + if (sender == controlP_lt) + { + p1 = result; + p2 = controlP_rb->GetWorldPosition(); + + double p_rt[3] = { p2[0],p1[1],0 }; + double p_lb[3] = { p1[0],p2[1],0 }; + controlP_rt->SetWorldPosition(p_rt); + controlP_lb->SetWorldPosition(p_lb); + } + else if (sender == controlP_rt) + { + p1 = result; + p2 = controlP_lb->GetWorldPosition(); + + double p_lt[3] = { p2[0],p1[1],0 }; + double p_rb[3] = { p1[0],p2[1],0 }; + controlP_lt->SetWorldPosition(p_lt); + controlP_rb->SetWorldPosition(p_rb); + + } + else if (sender == controlP_lb) + { + p1 = result; + p2 = controlP_rt->GetWorldPosition(); + + double p_lt[3] = { p1[0],p2[1],0 }; + double p_rb[3] = { p2[0],p1[1],0 }; + controlP_lt->SetWorldPosition(p_lt); + controlP_rb->SetWorldPosition(p_rb); + } + else if (sender == controlP_rb) + { + p1 = result; + p2 = controlP_lt->GetWorldPosition(); + + double p_rt[3] = { p1[0],p2[1],0 }; + double p_lb[3] = { p2[0],p1[1],0 }; + controlP_rt->SetWorldPosition(p_rt); + controlP_lb->SetWorldPosition(p_lb); + } + else + { + } + drawCircle(p1, p2); +} + + +void EllipseAnnotationActor::drawCircle(double *p1,double *p2) +{ + int CenterX = (p1[0] + p2[0]) / 2.0; + int CenterY = (p1[1] + p2[1]) / 2.0; + double r1 = abs(p1[0] - p2[0]) / 2.0; + double r2 = abs(p1[1] - p2[1]) / 2.0; + + double angle = 0; + int id = 0; + while (angle <= 2.0* M_PI + (2.0*M_PI / (Granularity*1.0))) + { + BaseDataPoints->SetPoint(id, r1 * cos(angle) + CenterX, r2 * sin(angle) + CenterY, 0); + angle = angle + (2.0*M_PI / (Granularity*1.0)); + id++; + } +} +void EllipseAnnotationActor::selfDragCb(vtkObject *, unsigned long, void *data) { +//control point drag realized by father +} +void EllipseAnnotationActor::controlPointsTransform(float x,float y){ + + //no need to trigger renderpoint repaint + DraggableActor::SafeDownCast(controlP_lt)->Transform(x, y); + DraggableActor::SafeDownCast(controlP_rt)->Transform(x, y); + DraggableActor::SafeDownCast(controlP_lb)->Transform(x, y); + DraggableActor::SafeDownCast(controlP_rb)->Transform(x, y); +} + +void EllipseAnnotationActor::controlPointsApplyTransform() { + //restore base point + DraggableActor::SafeDownCast(controlP_lt)->ApplyTransform(); + DraggableActor::SafeDownCast(controlP_rt)->ApplyTransform(); + DraggableActor::SafeDownCast(controlP_lb)->ApplyTransform(); + DraggableActor::SafeDownCast(controlP_rb)->ApplyTransform(); +} + + +void EllipseAnnotationActor::onMeasureMouseMove(vtkRenderWindowInteractor *iren) { + int x = iren->GetEventPosition()[0]; + int y = iren->GetEventPosition()[1]; + vtkRenderer *renderer = iren->FindPokedRenderer(x, y); + if (!renderer) return; + renderer->SetDisplayPoint(x, y, 0.0); + renderer->DisplayToWorld(); + + double *p_lt = controlP_lt->GetWorldPosition(); + double *p_rb = renderer->GetWorldPoint(); + + + //if ctrl key is pressed ,draw a standard circle + if (iren->GetControlKey()) { + double xlen = p_rb[0] - p_lt[0]; + double ylen = p_rb[1] - p_lt[1]; + if (abs(ylen) < abs(xlen)) + { + if (xlen > 0) { p_rb[0] = p_lt[0] + abs(ylen); } + if (xlen < 0) { p_rb[0] = p_lt[0] - abs(ylen); } + } + else + { + if (ylen > 0) { p_rb[1] = p_lt[1] + abs(xlen); } + if (ylen < 0) { p_rb[1] = p_lt[1] - abs(xlen); } + } + + } + + double p_rt[3] = { p_rb[0],p_lt[1],0 }; + double p_lb[3] = { p_lt[0],p_rb[1],0 }; + + controlP_rt->SetWorldPosition(p_rt); + controlP_lb->SetWorldPosition(p_lb); + controlP_rb->SetWorldPosition(p_rb); + + drawCircle(p_lt, p_rb); + //this->SetWorldPosition2(p); + iren->Render(); +} + +bool EllipseAnnotationActor::onMeasureLeftButtonDown(vtkRenderWindowInteractor *iren) { + int x = iren->GetEventPosition()[0]; + int y = iren->GetEventPosition()[1]; + vtkRenderer *renderer = iren->FindPokedRenderer(x, y); + if (!renderer) return false; + renderer->SetDisplayPoint(x, y, 0.0); + renderer->DisplayToWorld(); + double *p = renderer->GetWorldPoint(); + controlP_lt->SetWorldPosition(p); + controlP_lt->Highlight(0); + + controlP_rt->SetWorldPosition(p); + controlP_lb->SetWorldPosition(p); + controlP_rb->SetWorldPosition(p); + + for (int id = 0; id <= Granularity; id++) + { + BaseDataPoints->SetPoint(id, p[0], p[1], p[2]); + } + this->SetRenderer(renderer); + iren->Render(); + return true; +} + +bool EllipseAnnotationActor::onMeasureLeftButtonUp(vtkRenderWindowInteractor *iren) { + return false; +} + +void EllipseAnnotationActor::Transform(float x, float y) { + DraggableActor::Transform(x, y); + this->controlPointsTransform(x, y); +} + +void EllipseAnnotationActor::ApplyTransform() { + DraggableActor::ApplyTransform(); + this->controlPointsApplyTransform(); +} + +//void EllipseAnnotationActor::TransformOnly(float x, float y) { +// if (!tempStorePoints) { +// tempStorePoints = vtkPoints::New(); +// tempStorePoints->DeepCopy(renderPoints); +// transforming = true; +// } +// vtkNew trans; +// trans->Translate(x, y, 0); +// vtkNew filter; +// filter->SetTransform(trans); +// vtkNew poly; +// poly->SetPoints(tempStorePoints); +// filter->SetInputData(poly); +// filter->Update(); +// renderPoints->DeepCopy(filter->GetPolyDataOutput()->GetPoints()); +//} diff --git a/src/src/measure/EventsCenter.cpp b/src/src/measure/EventsCenter.cpp new file mode 100644 index 0000000..4e682bc --- /dev/null +++ b/src/src/measure/EventsCenter.cpp @@ -0,0 +1,7 @@ +// +// Created by 87714 on 2021/6/23. +// + +#include "EventsCenter.h" + +EventsCenter* EventsCenter::instance = new EventsCenter; \ No newline at end of file diff --git a/src/src/measure/HoverButton.cpp b/src/src/measure/HoverButton.cpp new file mode 100644 index 0000000..2b4ee86 --- /dev/null +++ b/src/src/measure/HoverButton.cpp @@ -0,0 +1,25 @@ +// +// Created by 87714 on 2021/7/22. +// + +#include "HoverButton.h" +#include "qdebug.h" +HoverButton::HoverButton(QWidget *parent) : + QPushButton(parent) +{ +} + +void HoverButton::leaveEvent(QEvent * event) { + QPushButton::leaveEvent(event); + emit endHover(); +} + +void HoverButton::enterEvent(QEvent * event) { + QPushButton::enterEvent( event); + emit hovered(); +} + +HoverButton::~HoverButton() { + +} + diff --git a/src/src/measure/KDicomObjects.cpp b/src/src/measure/KDicomObjects.cpp new file mode 100644 index 0000000..baf4f11 --- /dev/null +++ b/src/src/measure/KDicomObjects.cpp @@ -0,0 +1,112 @@ +// +// Created by KradChen on 2021/7/25. +// +#include "KDicomObjects.h" +#include +#include "dcmtk/dcmdata/dcfilefo.h" +#include "dcmtk/dcmdata/dcdatset.h" +#include +#include +#include +#include + + +#define ReadTagToObjectString(TagName, Object)\ +if (dataset->findAndGetOFString(DCM_##TagName, tempStr).good())\ +{\ + Object->TagName = tempStr.data();\ +}\ + + +void KDicomRoot::LoadFromDir(const char *path) { + OFList list; + OFString rpath = path; + #ifdef WIN32 + if (rpath[1] == ':' && rpath[2] == '/') rpath[2] = '\\'; + #endif + + size_t f = OFStandard::searchDirectoryRecursively(rpath, list); + + OFString patientName, patientID, patientBirthday, modality, area, SOPInstanceUid, + sex, studyInstanceUID, studyDate, description, seriesUid, seriesDescription, + tempStr; + this->Patients.clear(); + std::for_each(list.begin(), list.end(), [&](auto& item) { +#ifdef _DEBUG + printf("path:%s\r\n", item.data()); +#endif + DcmFileFormat file; + if (!file.loadFile(item).good()) return; + DcmDataset* dataset = file.getDataset(); + if (!dataset->findAndGetOFString(DCM_StudyInstanceUID, studyInstanceUID).good()) return; + if (!dataset->findAndGetOFString(DCM_PatientID, patientID).good()) return; + if (!dataset->findAndGetOFString(DCM_SeriesInstanceUID, seriesUid).good()) return; + if (!dataset->findAndGetOFString(DCM_SOPInstanceUID, SOPInstanceUid).good()) return; + if (this->Patients.find(patientID.data()) == this->Patients.end()) { + shared_ptr patient = make_shared(); + patient->PatientID = patientID.data(); + + ReadTagToObjectString(PatientName, patient); + ReadTagToObjectString(PatientBirthDate, patient); + ReadTagToObjectString(PatientSex, patient); + this->Patients[patientID.data()] = patient; + } + auto studys = &(this->Patients[patientID.data()]->Studys); + if (studys->find(studyInstanceUID.data()) == studys->end()) { + shared_ptr study = make_shared(); + study->Patient = this->Patients[patientID.data()].get(); + study->StudyUID = studyInstanceUID.data(); + ReadTagToObjectString(StudyDescription, study); + ReadTagToObjectString(Modality, study); + ReadTagToObjectString(StudyDate, study); + ReadTagToObjectString(StudyID, study); + ReadTagToObjectString(InstitutionName, study); + (*studys)[studyInstanceUID.data()] = study; + } + auto currentStudy = (*studys)[studyInstanceUID.data()]; + auto series = ¤tStudy->Series; + if (series->find(seriesUid.data()) == series->end()) { + shared_ptr nseries = make_shared(); + nseries->Study = currentStudy.get(); + nseries->seriesUID = seriesUid.data(); + nseries->StudyUID = studyInstanceUID.data(); + ReadTagToObjectString(AccessionNumber, nseries); + ReadTagToObjectString(SeriesDescription, nseries); + dataset->findAndGetSint32(DCM_AcquisitionNumber, nseries->AcquisitionNumber); + dataset->findAndGetSint32(DCM_SeriesNumber, nseries->SeriesNumber); + (*series)[seriesUid.data()] = nseries; + } + auto currentSeries = (*series)[seriesUid.data()]; + auto images = ¤tSeries->Images; + //avoid duplicate data + if (std::find_if(images->begin(), images->end(), + [SOPInstanceUid](shared_ptr img)->bool { + return img->SOPInstanceUID == SOPInstanceUid.data(); + } + ) != images->end()) + return; + shared_ptr image = make_shared(); + image->Series = currentSeries.get(); + image->RefFilePath = item.data(); + image->SOPInstanceUID = SOPInstanceUid.data(); + dataset->findAndGetSint32(DCM_InstanceNumber, image->InstanceNumber); + dataset->findAndGetUint16(DCM_Rows, image->Rows); + dataset->findAndGetUint16(DCM_Columns, image->Columns); + dataset->findAndGetFloat64(DCM_WindowWidth, image->WindowWidth); + dataset->findAndGetFloat64(DCM_WindowCenter, image->WindowCenter); + dataset->findAndGetFloat64(DCM_SliceThickness, image->SliceThickness); + dataset->findAndGetFloat64(DCM_ImagePositionPatient, image->PatientImagePosition[0], 0); + dataset->findAndGetFloat64(DCM_ImagePositionPatient, image->PatientImagePosition[1], 1); + dataset->findAndGetFloat64(DCM_ImagePositionPatient, image->PatientImagePosition[2], 2); + dataset->findAndGetFloat64(DCM_ImageOrientationPatient, image->PatientImageOrientation[0], 0); + dataset->findAndGetFloat64(DCM_ImageOrientationPatient, image->PatientImageOrientation[1], 1); + dataset->findAndGetFloat64(DCM_ImageOrientationPatient, image->PatientImageOrientation[2], 2); + dataset->findAndGetFloat64(DCM_ImageOrientationPatient, image->PatientImageOrientation[3], 3); + dataset->findAndGetFloat64(DCM_ImageOrientationPatient, image->PatientImageOrientation[4], 4); + dataset->findAndGetFloat64(DCM_ImageOrientationPatient, image->PatientImageOrientation[5], 5); + dataset->findAndGetFloat64(DCM_PixelSpacing, image->PixelSpacing[0], 0); + dataset->findAndGetFloat64(DCM_PixelSpacing, image->PixelSpacing[1], 1); + ReadTagToObjectString(SOPInstanceUID, image); + images->push_back(image); + }); +} diff --git a/src/src/measure/LineAnnotationActor.cpp b/src/src/measure/LineAnnotationActor.cpp new file mode 100644 index 0000000..9f279fa --- /dev/null +++ b/src/src/measure/LineAnnotationActor.cpp @@ -0,0 +1,127 @@ +// +// Created by 87714 on 2021/6/22. +// + +#include +#include "LineAnnotationActor.h" +#include "vtkObjectFactory.h" +#include "vtkPolyData.h" +#include "vtkCellArray.h" +#include "vtkProperty2D.h" +#include "vtkRenderer.h" +#include "vtkRenderWindow.h" +#include "ControlPointActor.h" + +vtkStandardNewMacro(LineAnnotationActor) + +void LineAnnotationActor::SetWorldPosition1(double x, double y, double z) { + BaseDataPoints->SetPoint(0, x, y, z); + +} + +void LineAnnotationActor::SetWorldPosition2(double x, double y, double z) { + BaseDataPoints->SetPoint(1, x, y, z); +} + +void LineAnnotationActor::BuildShape() { + if (!BaseDataPoints->GetNumberOfPoints()) return; + RebuildRenderPoint(); + vtkNew source; + source->SetPoints(renderPoints); + source->Update(); + renderData->DeepCopy(source->GetOutput()); + if (Measure::Hidden) + { + this->Hide(); + controlP1->Hide(); + controlP2->Hide(); + } + else{ + this->Show(); + controlP1->Show(); + controlP2->Show(); + } +} + +LineAnnotationActor::LineAnnotationActor() { + controlP1 = ControlPointActor::New(); + controlP2 = ControlPointActor::New(); + BaseDataPoints->SetNumberOfPoints(2); + BaseDataPoints->SetPoint(0, 0, 0, 0); + BaseDataPoints->SetPoint(1, 512, 512, 0); + controlP1->SetWorldPosition(0, 0, 0); + controlP2->SetWorldPosition(512, 512, 0); + renderPoints->SetNumberOfPoints(2); + controlP1->AddObserver(DraggableActorEvents::DragEvent, this, &LineAnnotationActor::controlPointCb); + controlP2->AddObserver(DraggableActorEvents::DragEvent, this, &LineAnnotationActor::controlPointCb); + this->AddObserver(DraggableActorEvents::DragEvent, this, &LineAnnotationActor::selfDragCb); +} + +LineAnnotationActor::~LineAnnotationActor() { + controlP1->Delete(); + controlP1 = nullptr; + controlP2->Delete(); + controlP2 = nullptr; + +} + +void LineAnnotationActor::SetRenderer(vtkRenderer *ren) { + DraggableActor::SetRenderer(ren); + controlP1->SetRenderer(ren); + controlP2->SetRenderer(ren); +} + +void LineAnnotationActor::controlPointCb(vtkObject *sender, unsigned long event, void *data) { + int index = sender == controlP1 ? 0 : 1; + vtkPoints *pts = static_cast(data); + double *pos = pts->GetPoint(0); + double result[3] = {0, 0, 0}; + GetSlicePlanePoint(pos[0], pos[1], this->Renderer, result); + BaseDataPoints->SetPoint(index, result); +} + +void LineAnnotationActor::selfDragCb(vtkObject *, unsigned long, void *data) { + vtkPoints *pts = static_cast(data); + double *pos = pts->GetPoint(0); + double result[3] = {0, 0, 0}; + GetSlicePlanePoint(pos[0], pos[1], this->Renderer, result); + controlP1->SetWorldPosition(result); + pos = pts->GetPoint(1); + GetSlicePlanePoint(pos[0], pos[1], this->Renderer, result); + controlP2->SetWorldPosition(result); +} + +void LineAnnotationActor::onMeasureMouseMove(vtkRenderWindowInteractor *iren) { + int x = iren->GetEventPosition()[0]; + int y = iren->GetEventPosition()[1]; + vtkRenderer *renderer = iren->FindPokedRenderer(x, y); + if (!renderer) return; + renderer->SetDisplayPoint(x, y, 0.0); + renderer->DisplayToWorld(); + double *p = renderer->GetWorldPoint(); + controlP2->SetWorldPosition(p); + this->SetWorldPosition2(p); + iren->Render(); +} + +bool LineAnnotationActor::onMeasureLeftButtonDown(vtkRenderWindowInteractor *iren) { + int x = iren->GetEventPosition()[0]; + int y = iren->GetEventPosition()[1]; + vtkRenderer *renderer = iren->FindPokedRenderer(x, y); + if (!renderer) return false; + renderer->SetDisplayPoint(x, y, 0.0); + renderer->DisplayToWorld(); + double *p = renderer->GetWorldPoint(); + controlP1->SetWorldPosition(p); + controlP1->Highlight(0); + controlP2->SetWorldPosition(p); + this->SetWorldPosition1(p); + this->SetWorldPosition2(p); + this->SetRenderer(renderer); + iren->Render(); + return true; +} + +bool LineAnnotationActor::onMeasureLeftButtonUp(vtkRenderWindowInteractor *iren) { + return false; +} diff --git a/src/src/measure/Measure.cpp b/src/src/measure/Measure.cpp new file mode 100644 index 0000000..b53149c --- /dev/null +++ b/src/src/measure/Measure.cpp @@ -0,0 +1,8 @@ +// +// Created by 87714 on 2021/7/18. +// + +#include "Measure.h" + +bool Measure::Hidden = false; + diff --git a/src/src/measure/MeasureStore.cpp b/src/src/measure/MeasureStore.cpp new file mode 100644 index 0000000..467f5eb --- /dev/null +++ b/src/src/measure/MeasureStore.cpp @@ -0,0 +1,71 @@ +// +// Created by 87714 on 2021/7/19. +// + +#include "MeasureStore.h" +#include "Measure.h" +MeasureStore* MeasureStore::instance = nullptr; + +void MeasureStore::Store(QString SeriesUid, int slice, Measure *measure) { + if (!store.contains(SeriesUid)){ + store[SeriesUid]=QMap>(); + } + if (!store[SeriesUid].contains(slice)){ + store[SeriesUid][slice] = QList(); + } + if(store[SeriesUid][slice].contains(measure)) return; + measure->SetMeasureID(QString("%1-%2").arg(SeriesUid).arg(slice)); + store[SeriesUid][slice].push_back(measure); + +} + +QList* MeasureStore::GetMeasures(QString SeriesUid, int slice) { + if (!store.contains(SeriesUid)) return nullptr; + if (!store[SeriesUid].contains(slice))return nullptr; + return &store[SeriesUid][slice]; +} + +void MeasureStore::Remove(Measure *measure) { + QString id = measure->GetMeasureID(); + QStringList list = id.split("-"); + measure->ForceDelete(); + store[list[0]][list[1].toInt()].removeOne(measure); + +} + +void MeasureStore::RemoveAllInSlice(QString SeriesUid,int slice) { + for (auto item:store[SeriesUid][slice]) { + item->ForceDelete(); + } + store[SeriesUid][slice].clear(); +} + +void MeasureStore::RemoveAllInSeries(QString SeriesUid) { + for (auto series:store[SeriesUid]) { + for (auto item: series) + { + item->ForceDelete(); + } + series.clear(); + } + store.remove(SeriesUid); +} + +void MeasureStore::Clear() { + for(auto item: store) + { + for (auto list :item) + { + for (auto m : list) + { + if (m){ + m->ForceDelete(); + m=nullptr; + } + } + list.clear(); + } + } +} + + diff --git a/src/src/measure/OpenPolyAnnotationActor.cpp b/src/src/measure/OpenPolyAnnotationActor.cpp new file mode 100644 index 0000000..041756b --- /dev/null +++ b/src/src/measure/OpenPolyAnnotationActor.cpp @@ -0,0 +1,121 @@ +// +// Created by 87714 on 2021/8/13. +// + +#include +#include +#include "OpenPolyAnnotationActor.h" +#include "ControlPointRActor.h" +#include "vtkRenderer.h" +vtkStandardNewMacro(OpenPolyAnnotationActor) + +void OpenPolyAnnotationActor::SetRenderer(vtkRenderer *ren) { + DraggableActor::SetRenderer(ren); + std::for_each(controlPointList.begin(), controlPointList.end(),[ren=ren](ControlPointRActor* iter){ + iter->SetRenderer(ren); + }); +} + +void OpenPolyAnnotationActor::BuildShape() { + if (!BaseDataPoints->GetNumberOfPoints()) return; + RebuildRenderPoint(); + vtkNew source; + source->SetClosed(this->Closed); + source->SetPoints(renderPoints); + source->Update(); + renderData->DeepCopy(source->GetOutput()); + if (Measure::Hidden) + { + this->Hide(); + std::for_each(controlPointList.begin(), controlPointList.end(),[=](ControlPointRActor* p){ + p->Hide(); + }); + } + else{ + this->Show(); + std::for_each(controlPointList.begin(), controlPointList.end(),[=](ControlPointRActor* p){ + p->Show(); + }); + } +} + +void OpenPolyAnnotationActor::SetPointWorldPosition(vtkIdType index, double x, double y, double z) { + + BaseDataPoints->SetPoint(index, x, y, z); +} + +bool OpenPolyAnnotationActor::onMeasureLeftButtonDown(vtkRenderWindowInteractor * iren) { + int x = iren->GetEventPosition()[0]; + int y = iren->GetEventPosition()[1]; + vtkRenderer *renderer = iren->FindPokedRenderer(x, y); + if (!renderer) return false; + renderer->SetDisplayPoint(x, y, 0.0); + renderer->DisplayToWorld(); + double *p = renderer->GetWorldPoint(); + auto controlP = ControlPointRActor::New(); + + controlP->SetWorldPosition(p); + controlP->Highlight(1); + controlP->AddObserver(DraggableActorEvents::DragEvent, this, &OpenPolyAnnotationActor::controlPointCb); + controlP->AddObserver(vtkCommand::EnterEvent, this, &OpenPolyAnnotationActor::HighlightOn); + controlP->AddObserver(vtkCommand::LeaveEvent, this, &OpenPolyAnnotationActor::HighlightOff); + controlPointList.push_back(controlP); + controlP->Index = BaseDataPoints->InsertNextPoint( p); + this->SetRenderer(renderer); + iren->Render(); + return true; +} + +void OpenPolyAnnotationActor::onMeasureMouseMove(vtkRenderWindowInteractor * iren) { + Measure::onMeasureMouseMove(iren); +} + +bool OpenPolyAnnotationActor::onMeasureLeftButtonUp(vtkRenderWindowInteractor * iren) { + return true; +} + +OpenPolyAnnotationActor::OpenPolyAnnotationActor() { + //auto controlP1 = ControlPointRActor::New(); + + BaseDataPoints->SetNumberOfPoints(0); + renderPoints->SetNumberOfPoints(0); + this->AddObserver(DraggableActorEvents::DragEvent, this, &OpenPolyAnnotationActor::selfDragCb); +} + +OpenPolyAnnotationActor::~OpenPolyAnnotationActor() { + std::for_each(controlPointList.begin(), controlPointList.end(),[=](ControlPointRActor* p){ + p->SetRenderer(nullptr); + p->Delete(); + }); + controlPointList.clear(); +} + +void OpenPolyAnnotationActor::selfDragCb(vtkObject *, unsigned long event, void *data) { + vtkPoints *pts = static_cast(data); + for (int i = 0; i < controlPointList.size(); ++i) { + double *pos = pts->GetPoint(i); + double result[3] = {0, 0, 0}; + GetSlicePlanePoint(pos[0], pos[1], this->Renderer, result); + controlPointList[i]->SetWorldPosition(result); + } +} + +void OpenPolyAnnotationActor::controlPointCb(vtkObject *sender, unsigned long event, void *data) { + auto index = ControlPointRActor::SafeDownCast(sender)->Index; + vtkPoints *pts = static_cast(data); + double *pos = pts->GetPoint(0); + double result[3] = {0, 0, 0}; + GetSlicePlanePoint(pos[0], pos[1], this->Renderer, result); + BaseDataPoints->SetPoint(index, result); +} + +void OpenPolyAnnotationActor::Highlight(int f) { + std::for_each(controlPointList.begin(), controlPointList.end(),[=](ControlPointRActor* p){ + p->Highlight(f); + }); + DraggableActor::Highlight(f); +} + +bool OpenPolyAnnotationActor::onMeasureDoubleClick(vtkRenderWindowInteractor *) { + return false; +} diff --git a/src/src/measure/RulerAnnotationActor.cpp b/src/src/measure/RulerAnnotationActor.cpp new file mode 100644 index 0000000..af0d6d7 --- /dev/null +++ b/src/src/measure/RulerAnnotationActor.cpp @@ -0,0 +1,78 @@ +// +// Created by 87714 on 2021/7/12. +// + +#include "RulerAnnotationActor.h" +#include "vtkObjectFactory.h" +#include "vtkPolyData.h" +#include "vtkProperty2D.h" +#include "vtkRenderer.h" +#include "vtkRenderWindow.h" +#include "ControlPointActor.h" +#include "vtkTextMapper.h" +#include "vtkTextProperty.h" +#include "ActorDraggableInteractorStyle.h" +#include "calibrationWidget.h" +vtkStandardNewMacro(RulerAnnotationActor) + +RulerAnnotationActor::RulerAnnotationActor() { + text = vtkActor2D::New(); + vtkNew textMapper; + textMapper->SetInput("0"); + text->SetMapper(textMapper); + textProperty = textMapper->GetTextProperty(); + textProperty->SetBold(true); + textProperty->SetFontFamilyToArial(); + textProperty->SetFontSize(16); + textProperty->SetColor(0.8, 0.8, 0.0); + textProperty->SetOpacity(0.75); + textProperty->SetFrame(false); + //textProperty->SetFrameColor(1.0,0.0,0.0); + textProperty->SetBackgroundColor(1.0, 0.0, 0.0); + textProperty->SetBackgroundOpacity(0.3); + this->AddObserver(ActorDraggableInteractorStyle::DraggableStyleEvents::RightButtonClickEvent, this, &RulerAnnotationActor::selfCalibCb); +} + +RulerAnnotationActor::~RulerAnnotationActor() { + textProperty = nullptr; + text->Delete(); + text = nullptr; +} + +void RulerAnnotationActor::BuildShape() { + LineAnnotationActor::BuildShape(); + double p1[3] = { 0,0,0 }; + double p2[3] = { 0,0,0 }; + BaseDataPoints->GetPoint(0, p1); + BaseDataPoints->GetPoint(1, p2); + p1[2] = 0.0; + p2[2] = 0.0; + real_dis = std::sqrt(vtkMath::Distance2BetweenPoints(p1, p2)); + + double dis; + if (isCalibration) + { + dis = calib_dis; + } + else + { + dis = real_dis; + } + char str[100]; + sprintf_s(str, "Distance:%.2f mm", dis); + double * rp = renderPoints->GetPoint(0); + if(isCalibration) + { + textProperty->SetBackgroundColor(0.0, 1.0, 0.0); + } + vtkTextMapper::SafeDownCast(text->GetMapper())->SetInput(str); + text->SetDisplayPosition(rp[0] + 10, rp[1] - 20); +} + +void RulerAnnotationActor::selfCalibCb(vtkObject *, unsigned long, void *data) +{ + vtkRenderWindowInteractor *iren = (vtkRenderWindowInteractor*)data; + calibrationWidget *w = new calibrationWidget(this, iren); + w->exec(); + delete w; +} \ No newline at end of file diff --git a/src/src/measure/RulerLegendActor.cpp b/src/src/measure/RulerLegendActor.cpp new file mode 100644 index 0000000..64f68a5 --- /dev/null +++ b/src/src/measure/RulerLegendActor.cpp @@ -0,0 +1,100 @@ +// +// Created by 87714 on 2021/9/2. +// + +#include +#include "RulerLegendActor.h" +#include "vtkPolyDataMapper2D.h" +#include "vtkRenderer.h" +#include "vtkRenderWindow.h" +#include "vtkCamera.h" +#include "vtkPoints.h" +#include "vtkProperty2D.h" + +vtkStandardNewMacro(RulerLegendActor) + +RulerLegendActor::RulerLegendActor() { + this->PickableOff(); + actor2D = vtkActor2D::New(); + actor2D->PickableOff(); + vtkNew mapper; + actor2D->SetMapper(mapper); + actor2D->GetProperty()->SetLineWidth(1.0); + actor2D->GetProperty()->SetColor(1.0,0.0,0.0); +} + +RulerLegendActor::~RulerLegendActor() { + actor2D->Delete(); + actor2D = nullptr; +} + +void RulerLegendActor::BuildShape(int* size, double scale) { + double halfHeight = ((double)size[1])/2.0; + double halfWidth = ((double)size[0])/2.0; + double pixelRepFactor = scale/halfHeight; + double begX = round(halfWidth-(50.0)/pixelRepFactor)+0.5; + double endX = round(halfWidth+(50.0)/pixelRepFactor)+0.5; + double step = (endX-begX)/10.0; + vtkNew pts; + vtkNew lines; + vtkIdType ids[2]={0,1}; + //horizontal + //add baseline + ids[0] = pts->InsertNextPoint(begX,2.5,0.0); + ids[1] = pts->InsertNextPoint(endX,2.5,0.0); + lines->InsertNextCell(2,ids); + //add a begin scale mark + vtkIdType temp = ids[1]; + ids[1] = pts->InsertNextPoint(begX,5.5,0.0); + lines->InsertNextCell(2,ids); + //add a end scale mark + ids[1] = temp; + ids[0] = pts->InsertNextPoint(endX,5.5,0.0); + lines->InsertNextCell(2,ids); + //add scale marks in the middle + for (int i=1; i<10;i++){ + double newX = round(begX+i*step)+0.5; + ids[0] = pts->InsertNextPoint(newX,2.5,0.0); + ids[1] = pts->InsertNextPoint(newX,5.5,0.0); + lines->InsertNextCell(2,ids); + } + //vertical + double begY = round(halfHeight-(50.0)/pixelRepFactor)+0.5; + double endY = round(halfHeight+(50.0)/pixelRepFactor)+0.5; + double stepY = (endX-begX)/10.0; + //add baseline + ids[0] = pts->InsertNextPoint(2.5,begY,0.0); + ids[1] = pts->InsertNextPoint(2.5,endY,0.0); + lines->InsertNextCell(2,ids); + //add a begin scale mark + temp = ids[1]; + ids[1] = pts->InsertNextPoint(5.5,begY,0.0); + lines->InsertNextCell(2,ids); + //add a end scale mark + ids[1] = temp; + ids[0] = pts->InsertNextPoint(5.5,endY,0.0); + lines->InsertNextCell(2,ids); + //add scale marks in the middle + for (int i=1; i<10;i++){ + double newY = round(begY+i*stepY)+0.5; + ids[0] = pts->InsertNextPoint(2.5,newY,0.0); + ids[1] = pts->InsertNextPoint(5.5,newY,0.0); + lines->InsertNextCell(2,ids); + } + renderData->SetPoints(pts); + renderData->SetLines(lines); + actor2D->GetMapper()->SetInputDataObject(renderData); +} + +void RulerLegendActor::ReleaseGraphicsResources(vtkWindow * iwin) { + actor2D->ReleaseGraphicsResources(iwin); +} + +int RulerLegendActor::RenderOverlay(vtkViewport *viewport) { + auto renderer = vtkRenderer::SafeDownCast(viewport); + if (!renderer) return 0; + int * size = renderer->GetRenderWindow()->GetSize(); + double scale = renderer->GetActiveCamera()->GetParallelScale(); + BuildShape(size,scale); + return actor2D->RenderOverlay(viewport); +}; diff --git a/src/src/measure/SeriesItem.cpp b/src/src/measure/SeriesItem.cpp new file mode 100644 index 0000000..6339bb6 --- /dev/null +++ b/src/src/measure/SeriesItem.cpp @@ -0,0 +1,33 @@ +// +// Created by 87714 on 2021/7/23. +// + +#include "SeriesItem.h" +#include +#include +SeriesItem::SeriesItem(QWidget *parent):QWidget(parent) { + this->setLayout(&mainLayout); + mainLayout.setAlignment(Qt::AlignTop); + mainLayout.setMargin(0); + mainLayout.setSpacing(0); + mainLayout.addWidget(&studyHeader); + studyHeader.setEnabled(false); +} + +SeriesItem::~SeriesItem() { + +} + +void SeriesItem::SetStudy(QString studyUid, QString studyDate, QString Modality) { + series_count++; + studyHeader.setText(QString("%1\r\n%2:%3").arg(studyDate).arg(Modality).arg(series_count)); +} + +void SeriesItem::AddSeries(QString seriesUid,QString des,int count) { + QPushButton* btn = new QPushButton(this); + btn->setObjectName("seriesicon"); + btn->setIcon(QIcon(":/Icon/image.png")); + btn->setIconSize(QSize(90,90)); + mainLayout.addWidget(btn); + +} diff --git a/src/src/measure/StudyItem.cpp b/src/src/measure/StudyItem.cpp new file mode 100644 index 0000000..eab15bc --- /dev/null +++ b/src/src/measure/StudyItem.cpp @@ -0,0 +1,53 @@ +// +// Created by 87714 on 2021/7/23. +// + +#include "StudyItem.h" +#include +#include +StudyItem::StudyItem(QWidget *parent): QWidget(parent) { + this->setLayout(&mainLayout); + mainLayout.setAlignment(Qt::AlignTop); + mainLayout.setMargin(0); + mainLayout.setSpacing(2); + mainLayout.addWidget(&studyHeader); + studyHeader.setObjectName("study"); + studyHeader.setEnabled(false); +} + +StudyItem::~StudyItem() { + +} + +void StudyItem::SetStudy(QString studyUid, QString studyDate,QString studyDes, QString Modality) { + this->studyUid = studyUid; + studyHeader.setText(QString("%1\r\n%2\r\n%3:%4").arg(studyDate).arg(studyDes).arg(Modality).arg(series_count)); +} + +void StudyItem::AddSeries(QString seriesUid, QString des,QPixmap img) { + series_count++; + QPushButton* btn = new QPushButton(this); + QVBoxLayout* vbox = new QVBoxLayout(this); + // vbox->setMargin(0); + btn->setObjectName("seriesicon"); + btn->setLayout(vbox); + if (!des.isEmpty()){ + QLabel* labeltext = new QLabel(this); + labeltext->setText(des.length()>13?(des.left(10)+"..."):des); + labeltext->setAlignment(Qt::AlignHCenter); + vbox->addWidget(labeltext); + } + QLabel* labelicon = new QLabel(this); + labelicon->setObjectName("seriesThumbnail"); + labelicon->setPixmap(img); + labelicon->setScaledContents(true); + labelicon->setFixedSize(85,85); + labelicon->setMargin(0); + labelicon->setAlignment(Qt::AlignHCenter); + vbox->addWidget(labelicon,0,Qt::AlignHCenter); + mainLayout.addWidget(btn); + connect(btn,&QPushButton::clicked,[=](){ + emit this->SeriesButtonClick(this->patientID,this->studyUid,seriesUid); + }); + +} diff --git a/src/src/measure/TextAnnotationActor.cpp b/src/src/measure/TextAnnotationActor.cpp new file mode 100644 index 0000000..5ef6e71 --- /dev/null +++ b/src/src/measure/TextAnnotationActor.cpp @@ -0,0 +1,151 @@ +// +// Created by 87714 on 2021/6/22. +// + +#include +#include "TextAnnotationActor.h" +#include "vtkObjectFactory.h" +#include "vtkPolyData.h" +#include "vtkCellArray.h" +#include "vtkProperty2D.h" +#include "vtkRenderer.h" +#include "vtkRenderWindow.h" +#include "ControlPointActor.h" +#include "pqFontPropertyWidget.h" +#include "ActorDraggableInteractorStyle.h" +vtkStandardNewMacro(TextAnnotationActor) + +void TextAnnotationActor::SetWorldPosition1(double x, double y, double z) { + BaseDataPoints->SetPoint(0, x, y, z); + +} + +void TextAnnotationActor::SetWorldPosition2(double x, double y, double z) { + BaseDataPoints->SetPoint(1, x, y, z); +} + +void TextAnnotationActor::BuildShape() { + if (!BaseDataPoints->GetNumberOfPoints()) return; + RebuildRenderPoint(); + if (Measure::Hidden) + { + this->Hide(); + } + else { + this->Show(); + } + double * rp = renderPoints->GetPoint(0); + vtkTextMapper::SafeDownCast(text->GetMapper())->SetInput(m_TextStr); + text->SetDisplayPosition(rp[0] + 10, rp[1] - 20); +} + +void TextAnnotationActor::SetTextInput(const char* str) +{ + strcpy_s(m_TextStr, str); + //BuildShape will be automatically called through render overlay. +} + + +void TextAnnotationActor::ResetTextProp() +{ + sprintf_s(m_TextStr, "Text"); + //vtkTextMapper::SafeDownCast(text->GetMapper())->SetInput(m_TextStr); + textProperty->SetBold(true); + textProperty->SetItalic(false); + textProperty->SetShadow(false); + + textProperty->SetFontFamilyToArial(); + textProperty->SetFontSize(16); + textProperty->SetColor(0.8, 0.8, 0.0); + textProperty->SetOpacity(0.75); + textProperty->SetFrame(false); + //textProperty->SetFrameColor(1.0, 0.0, 0.0); + textProperty->SetBackgroundColor(1.0, 0.0, 0.0); + textProperty->SetBackgroundOpacity(0.3); +} +TextAnnotationActor::TextAnnotationActor() { + BaseDataPoints->SetNumberOfPoints(2); + BaseDataPoints->SetPoint(0, 0, 0, 0); + BaseDataPoints->SetPoint(1, 512, 512, 0); + renderPoints->SetNumberOfPoints(2); + this->AddObserver(DraggableActorEvents::DragEvent, this, &TextAnnotationActor::selfDragCb); + this->AddObserver(ActorDraggableInteractorStyle::DraggableStyleEvents::PopPropEvent, this, &TextAnnotationActor::selfPickCb); + text = vtkActor2D::New(); + vtkNew textMapper; + //sprintf_s(m_TextStr, "Text"); + //textMapper->SetInput(m_TextStr); + text->SetMapper(textMapper); + textProperty = textMapper->GetTextProperty(); + ResetTextProp(); +} + + + +TextAnnotationActor::~TextAnnotationActor() { + textProperty = nullptr; + text->Delete(); + text = nullptr; + +} +void TextAnnotationActor::SetRenderer(vtkRenderer *ren) { + DraggableActor::SetRenderer(ren); +} + +void TextAnnotationActor::selfDragCb(vtkObject *, unsigned long, void *data) { + vtkPoints *pts = static_cast(data); + double *pos = pts->GetPoint(0); + double result[3] = { 0, 0, 0 }; + GetSlicePlanePoint(pos[0], pos[1], this->Renderer, result); + pos = pts->GetPoint(1); + GetSlicePlanePoint(pos[0], pos[1], this->Renderer, result); +} + +void TextAnnotationActor::selfPickCb(vtkObject *, unsigned long, void *data) { + + pqFontPropertyWidget::GetPropDlg()->SetTextActor(this); + pqFontPropertyWidget::GetPropDlg()->SetRenderWindowInteractor(m_iren); + pqFontPropertyWidget::GetPropDlg()->reloadProperties(); + pqFontPropertyWidget::GetPropDlg()->setModal(false); + pqFontPropertyWidget::GetPropDlg()->show(); +} + + +void TextAnnotationActor::onMeasureMouseMove(vtkRenderWindowInteractor *iren) { + int x = iren->GetEventPosition()[0]; + int y = iren->GetEventPosition()[1]; + vtkRenderer *renderer = iren->FindPokedRenderer(x, y); + if (!renderer) return; + renderer->SetDisplayPoint(x, y, 0.0); + renderer->DisplayToWorld(); + double *p = renderer->GetWorldPoint(); + this->SetWorldPosition2(p); + iren->Render(); +} + + +bool TextAnnotationActor::onMeasureLeftButtonDown(vtkRenderWindowInteractor *iren) { + //printf("pressed!\r\n"); + if (iren->GetRepeatCount()) + { + //printf("dbl clicked!\r\n"); + return true; + } + int x = iren->GetEventPosition()[0]; + int y = iren->GetEventPosition()[1]; + vtkRenderer *renderer = iren->FindPokedRenderer(x, y); + if (!renderer) return false; + renderer->SetDisplayPoint(x, y, 0.0); + renderer->DisplayToWorld(); + double *p = renderer->GetWorldPoint(); + this->SetWorldPosition1(p); + this->SetWorldPosition2(p); + + this->SetRenderer(renderer); + iren->Render(); + return true; +} + +bool TextAnnotationActor::onMeasureLeftButtonUp(vtkRenderWindowInteractor *iren) { + m_iren = iren; + return false; +} diff --git a/src/src/measure/calibrationWidget.cpp b/src/src/measure/calibrationWidget.cpp new file mode 100644 index 0000000..1a6a00b --- /dev/null +++ b/src/src/measure/calibrationWidget.cpp @@ -0,0 +1,30 @@ +#include "calibrationWidget.h" +#include "RulerAnnotationActor.h" +#include "vtkRenderWindowInteractor.h" + + + +calibrationWidget::calibrationWidget(RulerAnnotationActor *rulerActor, vtkRenderWindowInteractor* iren) + : QDialog(nullptr), _rulerActor(rulerActor), _rulerRender(iren) +{ + ui.setupUi(this); + this->setWindowIcon(QIcon(":/InfiniteViewer/Icon/logo.png")); + connect(ui.okButton, SIGNAL(clicked()), this, SLOT(SetTextInput())); + setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); + ui.led_TextInput->setText(QString::number(_rulerActor->getCalibration(), 10, 2)); +} + +calibrationWidget::~calibrationWidget() +{ +} + + +void calibrationWidget::SetTextInput() +{ + QString qstr = ui.led_TextInput->text(); + _rulerActor->setCalibration(qstr.toDouble()); + _rulerRender->Render(); +} + + + diff --git a/src/src/measure/pqFontPropertyWidget.cpp b/src/src/measure/pqFontPropertyWidget.cpp new file mode 100644 index 0000000..a925399 --- /dev/null +++ b/src/src/measure/pqFontPropertyWidget.cpp @@ -0,0 +1,269 @@ +#include "pqFontPropertyWidget.h" +#include "dicomimageview.h" +#include "QColorDialog.h" +//#include "QFontDialog.h" + +//pqFontPropertyWidget* pqFontPropertyWidget::instance = nullptr; +//pqFontPropertyWidget* pqFontPropertyWidget::GetInstance() { +// //lazy man +// if (!instance) { +// instance = new pqFontPropertyWidget(); +// } +// return instance; +//} + +pqFontPropertyWidget* pqFontPropertyWidget::propDlg = nullptr; + +pqFontPropertyWidget* pqFontPropertyWidget::GetPropDlg() { + //lazy man + if (!propDlg) { + propDlg = new pqFontPropertyWidget(); + } + return propDlg; +} + +pqFontPropertyWidget::pqFontPropertyWidget(QWidget *parent) + : QDialog(parent), textActor(nullptr), tRender(nullptr) +{ + ui.setupUi(this); + + ui.cbx_FontFamily->addItem("Arial"); + ui.cbx_FontFamily->addItem("Courier"); + ui.cbx_FontFamily->addItem("Times"); + + connect(ui.pbtn_Accept, SIGNAL(clicked()), this, SLOT(SetTextInput())); + connect(ui.pbtn_Reset, SIGNAL(clicked()), this, SLOT(ResetText())); + + connect(ui.cbx_FontFamily, SIGNAL(currentIndexChanged(int)), this, SLOT(SetFontFamily(int))); + connect(ui.spx_FontSize, SIGNAL(valueChanged(int)), this, SLOT(SetFontSize(int))); + connect(ui.dspx_Opacity, SIGNAL(valueChanged(double)), this, SLOT(SetOpacity(double))); + + connect(ui.tbtn_Bold, SIGNAL(clicked(bool)), this, SLOT(BoldOn(bool))); + connect(ui.tbnt_Italics, SIGNAL(clicked(bool)), this, SLOT(ItalicOn(bool))); + connect(ui.tbtn_Shadow, SIGNAL(clicked(bool)), this, SLOT(ShadowOn(bool))); + + connect(ui.pbtn_FontColor, SIGNAL(clicked()), this, SLOT(ColorChooser())); + //connect(ui.cbx_FontColor, SIGNAL(currentIndexChanged(int)), this, SLOT(ColorEditor())); + + setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); + + //QRect rect(0, 0, 32, 26); + //QRegion region(rect, QRegion::Ellipse); + //ui.pbtn_FontColor->setMask(region); +} + +pqFontPropertyWidget::~pqFontPropertyWidget() +{ + delete propDlg; +} + +void pqFontPropertyWidget::ResetText() +{ + if (this->textActor) + { + this->textActor->ResetTextProp(); + this->tRender->Render(); + reloadProperties(); + } +} + +void pqFontPropertyWidget::SetTextInput() +{ + if (this->textActor) + { + QString qstr = ui.led_TextInput->text(); + QByteArray ba = qstr.toLatin1(); + this->textActor->SetTextInput(ba.data()); + this->tRender->Render(); + } + + +} + + + + +//void pqFontPropertyWidget::ColorEditor() +//{ +// if (this->textActor) +// { +// QColor color = ui.cbx_FontColor->color(); +// this->textActor->GetTextPropty()->SetColor(color.red() / 255.0, color.green() / 255.0, color.blue() / 255.0); +// this->viewer->Render(); +// } +//} + + +void pqFontPropertyWidget::ColorChooser() +{ + QColor color = QColorDialog::getColor(Qt::white, this); + if (this->textActor) + { + this->textActor->GetTextProp()->SetColor(color.red() / 255.0, color.green() / 255.0, color.blue() / 255.0); + //ui.cbx_FontColor->setColor(color); + this->tRender->Render(); + } + char str[200]; + sprintf_s(str, + "background-color: rgb(%d, %d, %d);\ + border-style: solid;\ + border-width:2px;\ + border-radius:12px;\ + border-color: black;\ + max-width:20px;\ + max-height:20px;\ + min-width:20px;\ + min-height:20px;" + ,color.red(), color.green(), color.blue()); + ui.pbtn_FontColor->setStyleSheet(str); +} + + +void pqFontPropertyWidget::SetTextActor(TextAnnotationActor* t_actor) +{ + if (this->textActor) + { + this->textActor->GetTextProp()->SetFrame(false); + } + this->textActor = t_actor; + this->textActor->GetTextProp()->SetFrame(true); + this->textActor->GetTextProp()->SetFrameColor(1.0, 0.0, 0.0); +} + +void pqFontPropertyWidget::SetRenderWindowInteractor(vtkRenderWindowInteractor* iren) +{ + this->tRender = iren; + //reloadProperties(); +} + +//void pqFontPropertyWidget::SetViewer(DicomImageView* v) { +// this->viewer = v; +//} + +void pqFontPropertyWidget::reloadProperties() +{ + vtkTextProperty *tprop = this->textActor->GetTextProp(); + + //char * str = vtkTextMapper::SafeDownCast(this->textActor->GetTextProp())->GetInput(); + + ui.led_TextInput->setText(this->textActor->GetTextInput()); + ui.cbx_FontFamily->setCurrentIndex(tprop->GetFontFamily()); + ui.spx_FontSize->setValue(tprop->GetFontSize()); + ui.dspx_Opacity->setValue(tprop->GetOpacity()); + + ui.tbtn_Bold->setChecked(tprop->GetBold()); + ui.tbnt_Italics->setChecked(tprop->GetItalic()); + ui.tbtn_Shadow->setChecked(tprop->GetShadow()); + + + double *dcolor = tprop->GetColor(); + QColor color(dcolor[0]*255.0, dcolor[1] * 255.0, dcolor[2] * 255.0); + char str[200]; + sprintf_s(str, + "background-color: rgb(%d, %d, %d);\ + border-style: solid;\ + border-width:2px;\ + border-radius:12px;\ + border-color: black;\ + max-width:20px;\ + max-height:20px;\ + min-width:20px;\ + min-height:20px;" + , color.red(), color.green(), color.blue()); + ui.pbtn_FontColor->setStyleSheet(str); + +} + +void pqFontPropertyWidget::SetFontSize(int size_) { + if (this->textActor) + { + this->textActor->GetTextProp()->SetFontSize(size_); + this->tRender->Render(); + } +} + +void pqFontPropertyWidget::SetOpacity(double opacity_) +{ + if (this->textActor) + { + this->textActor->GetTextProp()->SetOpacity(opacity_); + this->tRender->Render(); + } + +} + +void pqFontPropertyWidget::SetFontFamily(int index) +{ + + if (this->textActor) + { + switch (index) + { + case(VTK_ARIAL): + this->textActor->GetTextProp()->SetFontFamilyToArial(); + break; + case(VTK_COURIER): + this->textActor->GetTextProp()->SetFontFamilyToCourier(); + break; + case(VTK_TIMES): + this->textActor->GetTextProp()->SetFontFamilyToTimes(); + break; + default: + this->textActor->GetTextProp()->SetFontFamilyToArial(); + break; + } + this->tRender->Render(); + } +} +void pqFontPropertyWidget::BoldOn(bool bold_) +{ + if (this->textActor) + { + if (bold_) + { + this->textActor->GetTextProp()->BoldOn(); + } + else + { + this->textActor->GetTextProp()->BoldOff(); + } + this->tRender->Render(); + } +} + +void pqFontPropertyWidget::ItalicOn(bool italic_) +{ + if (this->textActor) + { + if (italic_) + { + this->textActor->GetTextProp()->ItalicOn(); + } + else + { + this->textActor->GetTextProp()->ItalicOff(); + } + this->tRender->Render(); + } + +} + + +void pqFontPropertyWidget::ShadowOn(bool shadow_) +{ + if (this->textActor) + { + if (shadow_) + { + this->textActor->GetTextProp()->ShadowOn(); + } + else + { + this->textActor->GetTextProp()->ShadowOff(); + } + this->tRender->Render(); + } +} + + + diff --git a/src/src/measure/vtkArrow2DSource.cpp b/src/src/measure/vtkArrow2DSource.cpp new file mode 100644 index 0000000..5ec52e6 --- /dev/null +++ b/src/src/measure/vtkArrow2DSource.cpp @@ -0,0 +1,60 @@ +// +// Created by 87714 on 2021/8/11. +// + +#include "vtkArrow2DSource.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" + +//---------------------------------------------------------------------------- +vtkStandardNewMacro(vtkArrow2DSource); + +//---------------------------------------------------------------------------- +vtkArrow2DSource::vtkArrow2DSource() +{ + this->Closed = 0; +} + +//---------------------------------------------------------------------------- +vtkArrow2DSource::~vtkArrow2DSource() {} + +//---------------------------------------------------------------------------- +int vtkArrow2DSource::RequestData(vtkInformation* vtkNotUsed(request), + vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* outputVector) +{ + // get the info object + vtkInformation* outInfo = outputVector->GetInformationObject(0); + + // get the output + vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); + vtkIdType numPoints = this->GetNumberOfPoints(); + if (numPoints<2) return 1; + vtkNew pts; + vtkNew lines; + double p1[3] = {0.0, 0.0, 0.0}; + double p2[3] = {0.0, 0.0, 0.0}; + this->Points->GetPoint(0,p1); + this->Points->GetPoint(1,p2); + vtkIdType pids[2]; + pids[0] = pts->InsertNextPoint(p1[0],p1[1],0); + pids[1] =pts->InsertNextPoint(p2[0],p2[1],0); + lines->InsertNextCell(2,pids); + double angle = atan2(p1[1]-p2[1],p1[0]-p2[0])*180.0/vtkMath::Pi(); + double angle1 = (angle + 30) * vtkMath::Pi() / 180; + double angle2 = (angle - 30) * vtkMath::Pi() / 180; + double topX = TipLength * cos(angle1); + double topY = TipLength * sin(angle1); + double botX = TipLength* cos(angle2); + double botY = TipLength * sin(angle2); + pids[0] = pts->InsertNextPoint(p2[0]+topX,p2[1]+topY,0); + lines->InsertNextCell(2,pids); + pids[0] = pts->InsertNextPoint(p2[0]+botX,p2[1]+botY,0); + lines->InsertNextCell(2,pids); + output->SetPoints(pts); + output->SetLines(lines); + return 1; +} + +void vtkArrow2DSource::PrintSelf(ostream &os, vtkIndent indent) { + vtkPolyLineSource::PrintSelf(os, indent); +} diff --git a/src/src/measure/vtkSignalRaiser.cpp b/src/src/measure/vtkSignalRaiser.cpp new file mode 100644 index 0000000..91d6cae --- /dev/null +++ b/src/src/measure/vtkSignalRaiser.cpp @@ -0,0 +1,5 @@ +// +// Created by 87714 on 2021/7/23. +// + +#include "vtkSignalRaiser.h" diff --git a/src/src/util/ColorMapReader.cpp b/src/src/util/ColorMapReader.cpp new file mode 100644 index 0000000..5f1ac1d --- /dev/null +++ b/src/src/util/ColorMapReader.cpp @@ -0,0 +1,94 @@ +// +// Created by 87714 on 2021/9/4. +// + +#include "ColorMapReader.h" +#include +#include +#include +#include +#include +#include +#include +bool ColorMapReader::ParseFile(const char* path) { + + QFile f(path); + bool successs = f.open(QFile::ReadOnly); + if (false == successs) + { + return false; + } + + QByteArray buff = f.readAll(); + QJsonParseError error; + doc = QJsonDocument::fromJson(buff, &error); + + if (!doc.isArray()) return false; + QJsonArray ary = doc.array(); + int index = 0; + for (auto item : ary) + { + QJsonObject obj = item.toObject(); + if (obj.keys().contains("Name")) { + name_index_map[obj["Name"].toString().toStdString()] = index; + names.push_back(obj["Name"].toString().toStdString()); + } + index++; + } + return true; +} + +ColorMapReader::PresetType ColorMapReader::GetPresetType(const char *name) { + if(name_index_map.count(name)<1) return ColorMapReader::Undefined; + int index = name_index_map[name]; + QJsonArray ary = doc.array(); + if (index>=ary.size()) return ColorMapReader::Undefined; + QJsonObject obj = ary[index].toObject(); + if (obj.keys().contains("RGBPoints")) return ColorMapReader::RGBPoints; + if (obj.keys().contains("IndexedColors")) return ColorMapReader::IndexedColors; + return ColorMapReader::Undefined; +} + +const vector& ColorMapReader::GetPresetValues(const char *name) { + PresetType type = GetPresetType(name); + string key; + switch (type) { + case RGBPoints: + key = "RGBPoints"; + break; + case IndexedColors: + key = "IndexedColors"; + break; + default: + valStore.clear(); + return valStore; + } + QJsonArray ary = doc.array(); + int index = name_index_map[name]; + QJsonObject obj = ary[index].toObject(); + QJsonArray valary = obj[key.data()].toArray(); + valStore.resize(valary.size()); + int idx = 0; + for (auto v:valary) { + valStore[idx++] = v.toDouble(); + } + return valStore; +} + +double* ColorMapReader::GetPresetNanColor(const char *name) { + if(name_index_map.count(name)<1) return nullptr; + int index = name_index_map[name]; + QJsonArray ary = doc.array(); + if (index>=ary.size()) return nullptr; + QJsonObject obj = ary[index].toObject(); + if (obj.keys().contains("NanColor")){ + QJsonArray valary = obj["NanColor"].toArray(); + valStore.resize(ary.size()); + int idx = 0; + for (auto v:valary) { + valStore[idx++] = v.toDouble(); + } + return valStore.data(); + } + return nullptr; +} diff --git a/src/src/view/dicomimageview.cpp b/src/src/view/dicomimageview.cpp new file mode 100644 index 0000000..e807395 --- /dev/null +++ b/src/src/view/dicomimageview.cpp @@ -0,0 +1,914 @@ +锘#include "view/DicomImageView.h" +//#include +//#include +//#include "view/subview/metaDataWindow.h" +#include +#include +#include +#include "view/thumbnailImage.h" +#include +#include "vtkImageProperty.h" +#include "view/subview/mytitlebar.h" + +#include "ActorDraggableInteractorStyle.h" +#include "pqVCRController.h" +#include "pqVCRToolbar.h" +#include "vtkDiscretizableColorTransferFunction.h" +#include "metaDataWindow.h" + +#include "include_dcmtk.h" +//----------------------------------------------------------------------------- +DicomImageView::DicomImageView(QWidget* parent) + : QFrame(parent) +{ + + QFrame *wrapper = new QFrame(this); + wrapper->setObjectName("wrapper"); + + QGridLayout *view_layout = new QGridLayout(this); + _titleBar = createMyTitleBar(); + view_layout->addWidget(_titleBar, 0, 0); + view_layout->addWidget(wrapper, 1, 0); + view_layout->setContentsMargins(0, 0, 0, 0); + view_layout->setSpacing(0); + this->setLayout(view_layout); + + + QGridLayout* controlLayout = new QGridLayout(wrapper); + + _glWidt = new myQVTKOpenGLNativeWidget(wrapper); + + m_glrenWin = vtkSmartPointer ::New(); + //m_glrenWin->SetOffScreenRendering(1); //for offscreen rendering + _glWidt->SetRenderWindow(m_glrenWin);//set up interacte + //in order to visit in both way! + _glWidt->SetImageView(this); + + controlLayout->addWidget(_glWidt, 0, 0); + + + _scrollBar = new QScrollBar(Qt::Orientation::Vertical); + _scrollBar->setFocusPolicy(Qt::StrongFocus); + _scrollBar->setVisible(false); + _scrollBar->setObjectName("scrollbar"); + + + + + controlLayout->addWidget(_scrollBar, 0, 1); + + controlLayout->setContentsMargins(0, 0, 0, 0); + controlLayout->setSpacing(0); + wrapper->setLayout(controlLayout); + //this->setLayout(controlLayout); + + setAutoFillBackground(true); + + QWidget::setAcceptDrops(true); + this->setObjectName("dicomview"); + +} + +//----------------------------------------------------------------------------- +DicomImageView::~DicomImageView() +{ + _thread.quit();//event loop + _thread.wait(); //wait until return,block mode + _vcr_toolbar->deleteLater(); +} + + +bool DicomImageView::isVCRVisible() +{ + return _vcr_toolbar->isVisible(); +} + +void DicomImageView::setVCRVisible(bool visible) +{ + _vcr_toolbar->setVisible(visible); +} +void DicomImageView::cineModeOn() +{ + //updateVCRToolbarPos(); + int ax = (this->geometry().bottomLeft().x() + this->geometry().bottomRight().x()) / 2 + VCRHelper::getVCRXOffset(); + int ay = (this->geometry().bottomLeft().y() + this->geometry().bottomRight().y()) / 2 + VCRHelper::getVCRYOffset(); + _vcr_toolbar->move(ax, ay); + + _vcr_toolbar->show(); + this->initCineModeThread(); + _vcr_toolbar->reConnectController(_vcr_ctrl); + +} + +void DicomImageView::initCineModeThread() +{ + _vcr_ctrl = new pqVCRController(nullptr, this); + _vcr_ctrl->moveToThread(&_thread); + connect(&_thread, &QThread::finished, _vcr_ctrl, &QObject::deleteLater); + _thread.start(); + isCine = true; +} + + +MyTitleBar * DicomImageView::createMyTitleBar() +{ + MyTitleBar *titleBar = new MyTitleBar(this); + connect(titleBar, &MyTitleBar::signalButtonMaxClicked, this, &DicomImageView::Slot_viewDoubleclicked); + connect(titleBar, &MyTitleBar::signalButtonCloseClicked, this, &DicomImageView::Slot_ViewEmpty); + return titleBar; +} + + +void DicomImageView::AddSlice(int step) +{ + if (_ImageViewer == nullptr) + { + return; + } + if (HasSeries()) + { + int curSlice = _ImageViewer->GetSlice() + step; + _ImageViewer->SetSlice(curSlice); + _scrollBar->setValue(curSlice); + } +} + +void DicomImageView::onFirstFrame() +{ + if (HasSeries()) { + _scrollBar->setValue(_ImageViewer->GetSliceMin()); + } +} +void DicomImageView::onPreviousFrame() +{ + if (HasSeries()) { + int slice = _ImageViewer->GetSlice(); + slice = slice--; + int min_slice = _ImageViewer->GetSliceMin(); + if (slice < min_slice) + { + slice = _ImageViewer->GetSliceMax(); + } + _scrollBar->setValue(slice); + } +} +void DicomImageView::onNextFrame() +{ + if (HasSeries()) { + int slice = _ImageViewer->GetSlice(); + slice = slice++; + int max_slice = _ImageViewer->GetSliceMax(); + if (slice > max_slice) + { + slice = _ImageViewer->GetSliceMin(); + } + _scrollBar->setValue(slice); + } +} +void DicomImageView::onLastFrame() +{ + if (HasSeries()) { + _scrollBar->setValue(_ImageViewer->GetSliceMax()); + } +} + +void DicomImageView::SetSlice(int slice) +{ + if (_ImageViewer == nullptr) + { + return; + } + if (HasSeries()) + { + + _ImageViewer->SetSlice(slice); + //_ImageViewer->updateCornerInfo(TOP_LEFT); + _scrollBar->setValue(slice); + } +} + + +void DicomImageView::Slot_scrollValueChanged(int slice) +{ + //if the scroll bar is activated by user, emit + //if the scroll bar is activated by program, no emit + switch (_ScrollTriggerType) + { + case(scrollScope::TriggerType::USER_TRIGGER): + { + _ImageViewer->SetSlice(slice); //for interactor style use, reduce duplicate + // no break; + } + case(scrollScope::TriggerType::STYLE_TRIGGER): + { + _ImageViewer->updateCornerInfo(TOP_LEFT); + //invoke event + _SliceStep = slice - _PrevSlice; + _PrevSlice = slice; + //emit Signal_scrollValueChanged(this, slice); + int sliceArray[2]; + sliceArray[0] = slice; + sliceArray[1] = _SliceStep; + + this->Signal_SyncEvent(this, VTKIS_IMAGE_SLICING, sliceArray); + break; + } + case(scrollScope::TriggerType::SYNC_ONLY): + { + _PrevSlice = slice; + break; + } + default: + break; + } +} +void DicomImageView::updateCornerInfoPrivacy() +{ + if (HasSeries()) + { + _ImageViewer->updateCornerInfo(TOP_RIGHT); + } +} +void DicomImageView::wheelEvent(QWheelEvent *event) +{ + if (HasSeries()) { + int _Slice = _ImageViewer->GetSlice(); + int _MinSlice = _ImageViewer->GetSliceMin(); + int _MaxSlice = _ImageViewer->GetSliceMax(); + + if (event->delta() > 0) + { + // + //cout << "scroll forward" << endl; + if (_Slice > _MinSlice) + { + _Slice -= 1; + + //this->SetSlice(_Slice); + _scrollBar->setValue(_Slice); + + //emit Signal_scrollValueChanged(this, _Slice); + //emit scroll_ValueChanged(_Slice); + //mySetSlice(_Slice); + } + else + { + _Slice = _MinSlice; + //this->SetSlice(_Slice); + _scrollBar->setValue(_Slice); + } + } + else + { + + //cout << "scroll backward" << endl; + if (_Slice < _MaxSlice) + { + _Slice += 1; + //this->SetSlice(_Slice); + _scrollBar->setValue(_Slice); + //emit Signal_scrollValueChanged(this, _Slice); + //emit scroll_ValueChanged(_Slice); + //mySetSlice(_Slice); + } + else + { + _Slice = _MaxSlice; + //this->SetSlice(_Slice); + _scrollBar->setValue(_Slice); + } + } + } +} + + +void DicomImageView::Slot_ViewEmpty() +{ + emit Signal_ViewEmpty(this); +} + + +////----------------------------------------------------------------------------- +void DicomImageView::mousePressEvent(QMouseEvent* event) +{ + + emit Signal_ViewClicked(this); +} + +//----------------------------------------------------------------------------- +void DicomImageView::mouseMoveEvent(QMouseEvent* event) +{ + +} + +//----------------------------------------------------------------------------- +void DicomImageView::mouseReleaseEvent(QMouseEvent* event) +{ + +} + +//----------------------------------------------------------------------------- +void DicomImageView::mouseDoubleClickEvent(QMouseEvent* event) +{ + //Slot_viewDoubleclicked(); +} +void DicomImageView::Slot_viewDoubleclicked() +{ + //emit Signal_ViewClicked(this); + emit Signal_viewDoubleclicked(this); +} +//------------------------------------------------------- +/** + * @brief DicomImageView::dragEnterEvent + * 鎷栨嫿杩涘叆 + * @param e + */ +void DicomImageView::dragEnterEvent(QDragEnterEvent *e) { + if (e->mimeData()->hasFormat("text/plain")) { + e->acceptProposedAction(); + } +} + +//------------------------------------------------------- +/** + * @brief DicomImageView::dragMoveEvent + * 鎷栨嫿绉诲姩 + * @param e + */ +void DicomImageView::dragMoveEvent(QDragMoveEvent *e) { + if (e->mimeData()->hasFormat("text/plain")) { + e->acceptProposedAction(); + } +} + +//------------------------------------------------------- +/** + * @brief DicomImageView::dropEvent + * 鎷栨嫿鏉惧紑 + * @param e + */ +void DicomImageView::dropEvent(QDropEvent *e) { + if (e->mimeData()->hasFormat("text/plain")) { + e->acceptProposedAction(); + thumbnailImage *tb = qobject_cast( + (QObject *)(e->mimeData()->text().toULongLong())); + if (tb) { + //SetSeriesInstance(s); + //this->UpdataSeriesInstance(); + emit Signal_DragDropEvent(this, tb); + } + } +} +//------------------------------------------------------- +/** + * @brief DicomImageView::dragLeaveEvent + * 绂诲紑浜嬩欢 + * @param e + */ +void DicomImageView::dragLeaveEvent(QDragLeaveEvent *) { + return; +} + +void DicomImageView::getWindowLevel(double &level, double &width) +{ + if (HasSeries()) { + //_Series->getWindowLevel(level, width); + level = _ImageViewer->GetColorLevel(); + width = _ImageViewer->GetColorWindow(); + } + +} + +void DicomImageView::ToggleNegativeMode() +{ + if (HasSeries()) + { + if (isNegative) + { + _ImageViewer->SetNegativeMode(false); + isNegative = false; + } + else + { + _ImageViewer->SetNegativeMode(true); + isNegative = true; + } + + } +} + +void DicomImageView::setWindowLevel(double level, double width) +{ + if (HasSeries()) + { + + _ImageViewer->SetColorLevel(level); + _ImageViewer->SetColorWindow(width); + //You have to call updateConerInfo manually + //only mouse event can rely on callback + _ImageViewer->updateCornerInfo(BOTTOM_RIGHT); + emit Signal_WindowLevelEventForFusion(level, width); + + } +} + +void DicomImageView::initScrollbar() +{ + //_MinSlice = _ImageViewer->GetSliceMin(); + //_MaxSlice = _ImageViewer->GetSliceMax(); + _scrollBar->setValue(_ImageViewer->GetSliceMin()); + _scrollBar->setMaximum(_ImageViewer->GetSliceMax()); + _scrollBar->setSingleStep(1); + _scrollBar->setVisible(true); +} + + + + +void DicomImageView::ResetPanZoom() +{ + if (HasSeries()) + { + //necessary to reset pan + _ImageViewer->GetRenderer()->ResetCamera(); + //necessary to reset zoom + _ImageViewer->GetRenderer()->GetActiveCamera()->SetParallelScale(_Series->GetExtent()); + + } + +} + +void DicomImageView::ClearTransformations() +{ + if (HasSeries()) + { + int slice = _ImageViewer->GetSlice(); + ResetPanZoom(); + + double cameraPosition[3]; + double vup[3]; + _Series->getCameraCfg(vup, cameraPosition); + + //necessary to reset flip and rotate + _ImageViewer->GetRenderer()->GetActiveCamera()->SetPosition(cameraPosition); + _ImageViewer->GetRenderer()->GetActiveCamera()->SetViewUp(vup); + //avoid black out problem + _ImageViewer->GetRenderer()->ResetCameraClippingRange(); + _ImageViewer->SetSlice(slice); + //Render + _ImageViewer->GetRenderWindow()->Render(); + emit Signal_Transformation(CLEAR); + } +} + +void DicomImageView::HFlip() +{ + if (HasSeries()) { + int slice = _ImageViewer->GetSlice(); + ResetPanZoom(); + //HFlip + //_ImageViewer->GetRenderer()->GetActiveCamera()->SetViewUp(0, 1, 0); + _ImageViewer->GetRenderer()->GetActiveCamera()->Azimuth(180); + FlipExportHelper::toggleFlip(); + //to avoid black out problem during slicing + //slicing is related with rotation, you have to recalculate to get it right + _ImageViewer->GetRenderer()->ResetCameraClippingRange(); + _ImageViewer->SetSlice(slice); + + _ImageViewer->GetRenderWindow()->Render(); + emit Signal_Transformation(H_FLIP); + } +} + + +void DicomImageView::VFlip() +{ + if (HasSeries()) { + //double scale = _ImageViewer->GetRenderer()->GetActiveCamera()->GetParallelScale(); + + int slice = _ImageViewer->GetSlice(); + ResetPanZoom(); + //Method 2: Order matters + _ImageViewer->GetRenderer()->GetActiveCamera()->Elevation(-180); + _ImageViewer->GetRenderer()->GetActiveCamera()->Roll(180); + //_ImageViewer->GetRenderer()->GetActiveCamera()->SetViewUp(0,-1,0); + FlipExportHelper::toggleFlip(); + //to avoid black out problem during slicing + //slicing is related with rotation, you have to recalculate to get it right + + _ImageViewer->GetRenderer()->ResetCameraClippingRange(); + _ImageViewer->SetSlice(slice); + _ImageViewer->GetRenderWindow()->Render(); + emit Signal_Transformation(V_FLIP); + } + +} +void DicomImageView::Rotate(double angle, TransFormType operation) +{ + if (HasSeries()) { + int slice = _ImageViewer->GetSlice(); + ResetPanZoom(); + _ImageViewer->GetRenderer()->GetActiveCamera()->Roll(angle); + //to avoid black out problem during slicing + //slcing is related with rotation, you have to recalculate to get it right + _ImageViewer->GetRenderer()->ResetCameraClippingRange(); + _ImageViewer->SetSlice(slice); + _ImageViewer->GetRenderWindow()->Render(); + emit Signal_Transformation(operation); + } + +} + + +//NOTE: not working +void DicomImageView::setHighlight(bool yes) { + _titleBar->SetHighlight(yes); + +} +bool DicomImageView::IsFusion() +{ + return _ImageViewer->GetFusion(); + +} +void DicomImageView::SetFusionInput(DicomImageView *overlay) +{ + _overlay = overlay; + + vtkImageData *overlay_data = _overlay->getSeriesInstance()->GetData(); + + double window; + double level; + _overlay->getWindowLevel(level, window); + + _ImageViewer->FusionOn(); + _overlay->OverlayOn(); + _overlay->SetBaseView(this); + + _ImageViewer->SetFusionInputData(overlay_data); + _ImageViewer->SetFusionColorLeveL(level); + _ImageViewer->SetFusionColorWindow(window); + _ImageViewer->SetFusionOpacity(_ImageViewer->GetFusionOpacity()); + + // Example for vtkDiscretizableColorTransferFunction + vtkNew< vtkDiscretizableColorTransferFunction> table; + // example table1 d->r->g->w + table->AddRGBPoint(0.0, 0.0, 0.0, 0.0); + table->AddRGBPoint(0.33, 1.0, 0.0, 0.0); + table->AddRGBPoint(0.66, 1.0, 1.0, 0.0); + table->AddRGBPoint(1.0, 1.0, 1.0, 1.0); + _ImageViewer->SetFusionColorTable(table); + +} + +void DicomImageView::Slot_WindowLevelEventForFusion(double level, double width) +{ + if (IsFusion()) { + _ImageViewer->SetFusionColorLeveL(level); + _ImageViewer->SetFusionColorWindow(width); + _ImageViewer->Render(); + } +} + +void DicomImageView::IncreFusionOpacity(double percent) { + if (IsFusion()) { + _ImageViewer->IncreFusionOpacity(percent); + _ImageViewer->Render(); + } +} + +//@{ +/** +* Remove Fusion no matter it is base or overlay +*/ +void DicomImageView::removeViewWithFusion() +{ + if (HasSeries()) + { + if (IsFusion()) { + disconnect(_overlay, &DicomImageView::Signal_WindowLevelEventForFusion, + this, &DicomImageView::Slot_WindowLevelEventForFusion); + + this->removeFusion(); + _overlay->SetBaseView(nullptr); + _overlay = nullptr; + } + + if (IsOverlay()) { + disconnect(this, &DicomImageView::Signal_WindowLevelEventForFusion, + _base, &DicomImageView::Slot_WindowLevelEventForFusion); + + _base->removeFusion(); + _base->SetOverlayView(nullptr); + _base = nullptr; + + } + } +} + +void DicomImageView::removeFusion() +{ + _ImageViewer->RemoveFusionData(); + _ImageViewer->FusionOff(); + _ImageViewer->Render(); + _overlay->OverlayOff(); +} + +void DicomImageView::ActiveMeasure(Measure *m) +{ + if (nullptr != _Series) + { + _ImageViewer->ActiveMeasure(m); + } +} + +void DicomImageView::DeleteSelectedMeasure() +{ + if (nullptr != _Series) + { + _ImageViewer->DeleteSelectedMeasure(); + } +} +void DicomImageView::DeleteCurrentSliceMeasure() +{ + if (nullptr != _Series) + { + _ImageViewer->DeleteCurrentSliceMeasure(); + } +} +void DicomImageView::DeleteCurrentSeriesMeasure() +{ + if (nullptr != _Series) + { + _ImageViewer->DeleteCurrentSeriesMeasure(); + } +} +void DicomImageView::removeViewWithMeasure() +{ + _ImageViewer->UnActiveMeasure(); + _ImageViewer->DeleteCurrentSeriesMeasure(); +} + + + +void DicomImageView::setDicomImageView(SeriesInstance *series) +{ + //series->setVTKOpenGLNativeWidget(this->_glView); + CopyFromSeries(series); + //whenver change instance,set scroll value to zero + initScrollbar(); + + connect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(Slot_scrollValueChanged(int))); + connect(this, SIGNAL(Signal_Transformation(TransFormType)), this, SLOT(Slot_UpdateOrienInfo(TransFormType))); +} + +bool DicomImageView::HasSeries() +{ + if (nullptr == _Series) + { + return false; + } + return true; +} +void DicomImageView::Render() +{ + if (HasSeries()) + { + //_Series->Render(); + if (_ImageViewer->GetvtkCornerAnnotation()) { + _ImageViewer->GetvtkCornerAnnotation()->SetMaximumFontSize(FontSizeHelper::getSize(frameGeometry().size())); + } + _ImageViewer->Render(); + } + +} + + +void DicomImageView::CopyFromSeries(SeriesInstance *series) +{ + _Series = series; + _ImageViewer = series->getImageViewer2(); + //_CornerAnno = _ImageViewer->GetvtkCornerAnnotation(); + + + //create some callbacks + //m_syncEventCallback = vtkCallbackCommand::New(); + //m_syncEventCallback->SetCallback(syncEventFunc); + //m_syncEventCallback->SetClientData(this); + + ActorDraggableInteractorStyle *style = _ImageViewer->GetInteractorStyle(); + style->AddObserver(vtkCommand::EventIds::EndPanEvent, this, &DicomImageView::syncEventFunc); + //style->AddObserver(vtkCommand::EventIds::EndRotateEvent, this, &DicomImageView::syncEventFunc); + style->AddObserver(vtkCommand::EventIds::EndWindowLevelEvent, this, &DicomImageView::syncEventFunc); + style->AddObserver(ActorDraggableInteractorStyle::DraggableStyleEvents::EndDollyEvent, this, &DicomImageView::syncEventFunc); + style->AddObserver(ActorDraggableInteractorStyle::DraggableStyleEvents::SlicedEvent, this, &DicomImageView::syncEventFunc); + style->AddObserver(vtkCommand::EventIds::WindowLevelEvent, this, &DicomImageView::updateWindowLevelCb); + style->AddObserver(ActorDraggableInteractorStyle::DoubleClickEvent, this, &DicomImageView::doubleclickedEventCb); + style->AddObserver(ActorDraggableInteractorStyle::ScalarOpacityEvent, this, &DicomImageView::scalarEventCb); + style->AddObserver(ActorDraggableInteractorStyle::ScalarShiftEvent, this, &DicomImageView::scalarEventCb); +} + +void DicomImageView::updateWindowLevelCb(vtkObject*caller, unsigned long eid, void *calldata) +{ + _ImageViewer->updateCornerInfo(BOTTOM_RIGHT); + emit Signal_WindowLevelEventForFusion(_ImageViewer->GetColorLevel(), _ImageViewer->GetColorWindow()); +} + + + +void DicomImageView::scalarEventCb(vtkObject* sender, unsigned long eventId, void* calldata) +{ + double* r = (double*)calldata; + switch (eventId) + { + case(ActorDraggableInteractorStyle::ScalarShiftEvent): + qDebug() << "ScalarShiftEvent"; + _ImageViewer->SwitchToNextPreset(); + + break; + case(ActorDraggableInteractorStyle::ScalarOpacityEvent): + qDebug() << "ScalarOpacityEvent" << r[0]; + IncreFusionOpacity(r[0]); + break; + default: + + break; + } +} +void DicomImageView::doubleclickedEventCb(vtkObject* sender, unsigned long eventId, void* calldata) { + emit Signal_viewDoubleclicked(this); +} + +void DicomImageView::syncEventFunc(vtkObject*caller, unsigned long eid, void *calldata) +{ + + int* r = (int*)calldata; + switch (eid) + { + case(vtkCommand::EventIds::EndPanEvent): + this->Signal_SyncEvent(this, VTKIS_IMAGE_PAN, calldata); + break; + case(vtkCommand::EventIds::EndWindowLevelEvent): + //update corner info through callback + this->Signal_SyncEvent(this, VTKIS_IMAGE_WINDOWLEVEL, calldata); + break; + case(ActorDraggableInteractorStyle::DraggableStyleEvents::EndDollyEvent): + this->Signal_SyncEvent(this, VTKIS_IMAGE_ZOOM, calldata); + break; + case(ActorDraggableInteractorStyle::DraggableStyleEvents::SlicedEvent): + + //_Series->updateConerInfo(TOP_LEFT); + //this->m_scrl_active = false; + //_scrollBar->setValue(r[0]); + //r[1] = _SliceStep; //must after slot scrollValueChanged + //this->m_scrl_active = true; + //this->Signal_ViewChanged(this, VTKIS_IMAGE_SLICING, calldata); + + _scrollBar->setValue(r[0]); + + break; + default: + break; + } + +} + + +// +//void DicomImageView::syncPan(vtkObject*caller, unsigned long eid, void* clientdata, void *calldata) +//{ +// DicomImageView *viewer = static_cast(clientdata); +// +// //double level; +// //double width; +// //viewer->getWindowLevel(level, width); +// //viewer->Signal_WindowLevelChanged(viewer, level, width); +//} + +void DicomImageView::orphanizeSeriesInstance() +{ + //this will be the last instance ,only serve for copy + _ImageViewer->GetRenderer()->RemoveAllViewProps(); + this->Render(); +} +void DicomImageView::ResetView() +{ + + if (HasSeries()) + { + + removeViewWithFusion(); + removeViewWithMeasure(); + + + + + orphanizeSeriesInstance(); + //_glWidt->update(); + + //_scrollBar = nullptr; + _ImageViewer = nullptr; + _Series = nullptr; + + _ScrollTriggerType = scrollScope::TriggerType::USER_TRIGGER; + //_Slice = 0; + _PrevSlice = 0; + _SliceStep = 0; + + } + + _scrollBar->setVisible(false); + + if (isCine) + { + _vcr_toolbar->setVisible(false); + } +} +int DicomImageView::getSeriesNumber() +{ + if (HasSeries()) + { + return _Series->GetSeriesNumber(); + } + return -1; +} +void DicomImageView::updateCornerInfoAll() +{ + if (HasSeries()) + { + _ImageViewer->updateCornerInfoAll(); + } +} +void DicomImageView::ShowMetaData() +{ + + QString fileName = QString::fromLatin1(this->_Series->getCurImageName()); + if (!fileName.isEmpty()) { + + //DcmFileFormat *dcmFile = new DcmFileFormat; + DcmFileFormat dcmFile; + if (!dcmFile.loadFile(fileName.toStdString()).good()) + { + QMessageBox::warning(this, + tr("Error"), + tr("Unable to read file %1").arg(fileName), + QMessageBox::Ok); + return; + } + //DcmDataset* dataset = dcmFile.getDataset(); + metaDataWindow *dicomWindow = new metaDataWindow(&dcmFile); + dicomWindow->setAttribute(Qt::WA_DeleteOnClose); + dicomWindow->setWindowTitle("DICOM Tags ("+fileName+")"); + dicomWindow->setWindowFilePath(fileName); + //dicomWindow->resize(dicomWindow->sizeHint()); + dicomWindow->resize(USER_CONFIG::DEFAULT_TAG_WINDOW_SIZE); + //this->centralWidget->addSubWindow(dicomWindow); + dicomWindow->show(); + + } +} + +void DicomImageView::resizeEvent(QResizeEvent *event) +{ + //auto size conner info + if (!_ImageViewer) return; + if (_ImageViewer->GetvtkCornerAnnotation()) { + _ImageViewer->GetvtkCornerAnnotation()->SetMaximumFontSize(FontSizeHelper::getSize(frameGeometry().size())); + _ImageViewer->Render(); + } + if (isCine) + { + //if (_vcr_toolbar->isVisible()) + //we see the mountains as moutains,hh + { + int ax = (this->geometry().bottomLeft().x() + this->geometry().bottomRight().x()) / 2 + VCRHelper::getVCRXOffset(); + int ay = (this->geometry().bottomLeft().y() + this->geometry().bottomRight().y()) / 2 + VCRHelper::getVCRYOffset();; + _vcr_toolbar->move(ax, ay); + } + } + +} + +void DicomImageView::SetZoomScale(double scale) +{ + if (HasSeries()) + { + _ImageViewer->SetZoomScale(scale); + _ImageViewer->Render(); + } +} + +void DicomImageView::SetPanOffset(double * pan) +{ + if (HasSeries()) + { + + _ImageViewer->SetPanOffset(pan); + _ImageViewer->Render(); + } +} + + +void DicomImageView::Slot_UpdateOrienInfo(TransFormType operation) +{ + _ImageViewer->updateOrienInfo(operation); +} \ No newline at end of file diff --git a/src/src/view/myQVTKOpenGLNativeWidget.cpp b/src/src/view/myQVTKOpenGLNativeWidget.cpp new file mode 100644 index 0000000..bb2b3f8 --- /dev/null +++ b/src/src/view/myQVTKOpenGLNativeWidget.cpp @@ -0,0 +1,821 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: myQVTKOpenGLNativeWidget.cxx + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "view/myQVTKOpenGLNativeWidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "QVTKInteractor.h" +#include "QVTKInteractorAdapter.h" +#include "vtkCommand.h" +#include "vtkGenericOpenGLRenderWindow.h" +#include "vtkInteractorStyleTrackballCamera.h" +#include "vtkNew.h" +#include "vtkObjectFactory.h" +#include "vtkOpenGLState.h" + +#ifdef __APPLE__ +#include "QVTKOpenGLWidget.h" +#include + +/** + * Unused except on MacOS. + * In an application using both QVTKOpenGLWidget + * and myQVTKOpenGLNativeWidget, this bug can appear: + * https://bugreports.qt.io/browse/QTBUG-69644 + */ +namespace +{ + /** + * It is needed to switch the visibility of currently visible QVTKOpenGLWidget + * back and forth (ie hide then show). Just after initialization of myQVTKOpenGLNativeWidget + * (ie when we receive WindowActivate event, or PolishRequest event) + * This method takes care of it. + */ + void QVTKOpenGLWidgetMacOSCheck(QWidget* window) + { + // Used a static to ensure the fix is done only once + static bool QVTKOpenGLWidgetMacOSFixed = false; + + if (QVTKOpenGLWidgetMacOSFixed || !window) + { + return; + } + + // Switch visibility back and forth of visible QVTKOpenGLWidgets + // that share the same window as this myQVTKOpenGLNativeWidget. + // This ensures they come back on the front after the Qt bug happens. + auto widgets = window->findChildren(); + for (auto qvglWidget : widgets) + { + if (qvglWidget->isVisible()) + { + qvglWidget->hide(); + qvglWidget->show(); + QVTKOpenGLWidgetMacOSFixed = true; + } + } + } +} +#endif // __APPLE__ + +// #define DEBUG_QVTKOPENGL_WIDGET +#ifdef DEBUG_QVTKOPENGL_WIDGET +#define vtkmyQVTKOpenGLNativeWidgetDebugMacro(msg) \ + cout << this << ": " msg << endl; \ + if (this->Logger) \ + { \ + this->Logger->logMessage( \ + QOpenGLDebugMessage::createApplicationMessage(QStringLiteral("myQVTKOpenGLNativeWidget::" msg))); \ + } +#else +#define vtkmyQVTKOpenGLNativeWidgetDebugMacro(x) +#endif + +class QVTKOpenGLNativeWidgetObserver : public vtkCommand +{ +public: + static QVTKOpenGLNativeWidgetObserver* New() { return new QVTKOpenGLNativeWidgetObserver(); } + vtkTypeMacro(QVTKOpenGLNativeWidgetObserver, vtkCommand); + + void SetTarget(myQVTKOpenGLNativeWidget* target) { this->Target = target; } + + void Execute(vtkObject* object, unsigned long eventId, void* callData) override + { + if (this->Target) + { + switch (eventId) + { + case vtkCommand::WindowMakeCurrentEvent: + { + // We do not call QOpenGLWidget::makeCurrent() as that also makes the + // frame buffer object used by QOpenGLWidget active. This can have + // unintended side effects when MakeCurrent gets called in a + // render-pass, for example. We should only be making the context + // active. To do that, we use this trick. We rely on the + // QOpenGLContext have been called makeCurrent() previously so we + // can get to the surface that was used to do that. We simply + // reactivate on that surface. + QOpenGLContext* ctxt = this->Target->context(); + QSurface* surface = ctxt ? ctxt->surface() : nullptr; + if (surface) + { + ctxt->makeCurrent(surface); + } + Q_ASSERT(ctxt == nullptr || surface != nullptr); + } + break; + + case vtkCommand::WindowIsCurrentEvent: + { + bool& cstatus = *reinterpret_cast(callData); + cstatus = (QOpenGLContext::currentContext() == this->Target->context()); + } + break; + + case vtkCommand::WindowFrameEvent: + this->Target->windowFrameEventCallback(); + break; + + case vtkCommand::StartEvent: + VTK_FALLTHROUGH; + case vtkCommand::StartPickEvent: + this->Target->startEventCallback(); + break; + case vtkCommand::CursorChangedEvent: + this->Target->cursorChangedCallback(object, eventId, nullptr, callData); + break; + } + } + } + +protected: + QVTKOpenGLNativeWidgetObserver() {} + ~QVTKOpenGLNativeWidgetObserver() override {} + QPointer Target; +}; + +// Tolerance used when truncating the device pixel ratio scaled +// window size in calls to SetSize / SetPosition. +const double myQVTKOpenGLNativeWidget::DevicePixelRatioTolerance = 1e-5; + +//----------------------------------------------------------------------------- +myQVTKOpenGLNativeWidget::myQVTKOpenGLNativeWidget(QWidget* parentWdg, Qt::WindowFlags f) + : Superclass(parentWdg, f) + , InteractorAdapter(nullptr) + , EnableHiDPI(false) + , OriginalDPI(0) + , FBO(nullptr) + , InPaintGL(false) + , DoVTKRenderInPaintGL(false) + , Logger(nullptr) +{ + this->Observer->SetTarget(this); + + // default to strong focus + this->setFocusPolicy(Qt::StrongFocus); + + this->setUpdateBehavior(QOpenGLWidget::PartialUpdate); + + this->InteractorAdapter = new QVTKInteractorAdapter(this); + this->InteractorAdapter->SetDevicePixelRatio(this->devicePixelRatio()); + + this->setMouseTracking(true); + + // QOpenGLWidget::resized() is triggered when the default FBO in QOpenGLWidget is recreated. + // We use the same signal to recreate our FBO. + this->connect(this, SIGNAL(resized()), SLOT(recreateFBO())); +} + +//----------------------------------------------------------------------------- +myQVTKOpenGLNativeWidget::~myQVTKOpenGLNativeWidget() +{ + vtkmyQVTKOpenGLNativeWidgetDebugMacro("~myQVTKOpenGLNativeWidget"); + // essential to cleanup context so that the render window finalizes and + // releases any graphics resources it may have allocated. + this->cleanupContext(); + this->SetRenderWindow(static_cast(nullptr)); + this->Observer->SetTarget(nullptr); + delete this->InteractorAdapter; + delete this->Logger; +} + +void myQVTKOpenGLNativeWidget::SetImageView(DicomImageView *v) +{ + this->view = v; +} + +DicomImageView* myQVTKOpenGLNativeWidget::GetImageView() +{ + return this->view; +} +//----------------------------------------------------------------------------- +void myQVTKOpenGLNativeWidget::SetRenderWindow(vtkRenderWindow* win) +{ + vtkGenericOpenGLRenderWindow* gwin = vtkGenericOpenGLRenderWindow::SafeDownCast(win); + this->SetRenderWindow(gwin); + if (gwin == nullptr && win != nullptr) + { + qDebug() << "myQVTKOpenGLNativeWidget requires a `vtkGenericOpenGLRenderWindow`. `" + << win->GetClassName() << "` is not supported."; + } +} + +//----------------------------------------------------------------------------- +void myQVTKOpenGLNativeWidget::SetRenderWindow(vtkGenericOpenGLRenderWindow* win) +{ + if (this->RenderWindow == win) + { + return; + } + + if (this->RenderWindow) + { + this->RenderWindow->RemoveObserver(this->Observer); + this->RenderWindow->SetReadyForRendering(false); + } + this->RenderWindow = win; + this->requireRenderWindowInitialization(); + if (this->RenderWindow) + { + // set this to 0 to reinitialize it before setting the RenderWindow DPI + this->OriginalDPI = 0; + + // if an interactor wasn't provided, we'll make one by default + if (!this->RenderWindow->GetInteractor()) + { + // create a default interactor + vtkNew iren; + // iren->SetUseTDx(this->UseTDx); + this->RenderWindow->SetInteractor(iren); + iren->Initialize(); + + // now set the default style + vtkNew style; + iren->SetInteractorStyle(style); + } + + this->RenderWindow->AddObserver(vtkCommand::WindowMakeCurrentEvent, this->Observer); + this->RenderWindow->AddObserver(vtkCommand::WindowIsCurrentEvent, this->Observer); + this->RenderWindow->AddObserver(vtkCommand::WindowFrameEvent, this->Observer); + this->RenderWindow->AddObserver(vtkCommand::StartEvent, this->Observer); + this->RenderWindow->AddObserver(vtkCommand::StartPickEvent, this->Observer); + this->RenderWindow->AddObserver(vtkCommand::StartPickEvent, this->Observer); + this->RenderWindow->AddObserver(vtkCommand::CursorChangedEvent, this->Observer); + + if (this->FBO) + { + this->makeCurrent(); + this->recreateFBO(); + } + } +} + +//----------------------------------------------------------------------------- +void myQVTKOpenGLNativeWidget::startEventCallback() +{ + vtkmyQVTKOpenGLNativeWidgetDebugMacro("startEventCallback"); + this->makeCurrent(); + if (this->FBO) + { + // ensure that before vtkRenderWindow starts to render, we activate the FBO + // to render into. VTK code can be a bit lax with it. This just ensures that + // we have the FBO activated. + this->FBO->bind(); + } +} + +//----------------------------------------------------------------------------- +vtkRenderWindow* myQVTKOpenGLNativeWidget::GetRenderWindow() +{ + if (!this->RenderWindow) + { + // create a default + vtkGenericOpenGLRenderWindow* win = vtkGenericOpenGLRenderWindow::New(); + this->SetRenderWindow(win); + win->Delete(); + } + return this->RenderWindow; +} + +//----------------------------------------------------------------------------- +QVTKInteractor* myQVTKOpenGLNativeWidget::GetInteractor() +{ + return QVTKInteractor::SafeDownCast(this->GetRenderWindow()->GetInteractor()); +} + +//----------------------------------------------------------------------------- +void myQVTKOpenGLNativeWidget::copyFromFormat(const QSurfaceFormat& format, vtkRenderWindow* win) +{ + if (vtkOpenGLRenderWindow* oglWin = vtkOpenGLRenderWindow::SafeDownCast(win)) + { + oglWin->SetStereoCapableWindow(format.stereo() ? 1 : 0); + // samples may not be correct if format is obtained from + // QOpenGLWidget::format() after the context is created. That's because + // QOpenGLWidget always created context to samples=0. + oglWin->SetMultiSamples(format.samples()); + oglWin->SetStencilCapable(format.stencilBufferSize() > 0); + } +} + +//----------------------------------------------------------------------------- +void myQVTKOpenGLNativeWidget::copyToFormat(vtkRenderWindow* win, QSurfaceFormat& format) +{ + if (vtkOpenGLRenderWindow* oglWin = vtkOpenGLRenderWindow::SafeDownCast(win)) + { + format.setStereo(oglWin->GetStereoCapableWindow()); + format.setSamples(oglWin->GetMultiSamples()); + format.setStencilBufferSize(oglWin->GetStencilCapable() ? 8 : 0); + } +} + +//----------------------------------------------------------------------------- +QSurfaceFormat myQVTKOpenGLNativeWidget::defaultFormat() +{ + QSurfaceFormat fmt; + fmt.setRenderableType(QSurfaceFormat::OpenGL); + fmt.setVersion(3, 2); + fmt.setProfile(QSurfaceFormat::CoreProfile); + fmt.setSwapBehavior(QSurfaceFormat::DoubleBuffer); + fmt.setRedBufferSize(1); + fmt.setGreenBufferSize(1); + fmt.setBlueBufferSize(1); + fmt.setDepthBufferSize(1); + fmt.setStencilBufferSize(0); + fmt.setAlphaBufferSize(1); + fmt.setStereo(false); + fmt.setSamples(vtkOpenGLRenderWindow::GetGlobalMaximumNumberOfMultiSamples()); +#ifdef DEBUG_QVTKOPENGL_WIDGET + fmt.setOption(QSurfaceFormat::DebugContext); +#endif + return fmt; +} + +//----------------------------------------------------------------------------- +void myQVTKOpenGLNativeWidget::setEnableHiDPI(bool enable) +{ + this->EnableHiDPI = enable; + + if (this->RenderWindow) + { + if (this->OriginalDPI == 0) + { + this->OriginalDPI = this->RenderWindow->GetDPI(); + } + if (this->EnableHiDPI) + { + this->RenderWindow->SetDPI(this->OriginalDPI * this->devicePixelRatio()); + } + else + { + this->RenderWindow->SetDPI(this->OriginalDPI); + } + } +} + +//----------------------------------------------------------------------------- +void myQVTKOpenGLNativeWidget::setQVTKCursor(const QCursor &cursor) +{ + this->setCursor(cursor); +} + +//----------------------------------------------------------------------------- +void myQVTKOpenGLNativeWidget::recreateFBO() +{ + vtkmyQVTKOpenGLNativeWidgetDebugMacro("recreateFBO"); + delete this->FBO; + this->FBO = nullptr; + if (!this->RenderWindow) + { + return; + } + + // Since myQVTKOpenGLNativeWidget::initializeGL() cannot set multi-samples + // state on the RenderWindow correctly, we do it here. + QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); + GLint samples; + f->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); + + // Some graphics drivers report the number of samples as 1 when + // multisampling is off. Set the number of samples to 0 in this case. + samples = samples > 1 ? samples : 0; + this->RenderWindow->SetMultiSamples(static_cast(samples)); + + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::Depth); + format.setSamples(samples); + +#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0) + // Qt < 5.6 only has an integer API for device pixel ratio. + const double devicePixelRatio_ = this->devicePixelRatio(); +#else + const double devicePixelRatio_ = this->devicePixelRatioF(); +#endif + const QSize widgetSize = this->size(); + const QSize deviceSize = QSize(static_cast(widgetSize.width() * devicePixelRatio_ + DevicePixelRatioTolerance), + static_cast(widgetSize.height() * devicePixelRatio_ + DevicePixelRatioTolerance)); + + // This is as good an opportunity as any to communicate size to the render + // window. + this->InteractorAdapter->SetDevicePixelRatio(devicePixelRatio_); + if (vtkRenderWindowInteractor* iren = this->RenderWindow->GetInteractor()) + { + iren->SetSize(deviceSize.width(), deviceSize.height()); + } + this->RenderWindow->SetSize(deviceSize.width(), deviceSize.height()); + this->RenderWindow->SetPosition(static_cast(this->x() * devicePixelRatio_ + DevicePixelRatioTolerance), + static_cast(this->y() * devicePixelRatio_ + DevicePixelRatioTolerance)); + + // Set screen size on render window. + const QRect screenGeometry = QApplication::desktop()->screenGeometry(this); + this->RenderWindow->SetScreenSize(screenGeometry.width(), screenGeometry.height()); + + this->FBO = new QOpenGLFramebufferObject(deviceSize, format); + this->FBO->bind(); + this->RenderWindow->SetForceMaximumHardwareLineWidth(1); + this->RenderWindow->SetReadyForRendering(true); + this->RenderWindow->InitializeFromCurrentContext(); + + this->setEnableHiDPI(this->EnableHiDPI); + + // Since the context or frame buffer was recreated, if a paintGL call ensues, + // we need to ensure we're requesting VTK to render. + this->DoVTKRenderInPaintGL = true; + + // Clear to ensure that an uninitialized framebuffer is never displayed. + vtkOpenGLState *ostate = this->RenderWindow->GetState(); + // have to keep vtk state up to date as well + ostate->vtkglDisable(GL_SCISSOR_TEST); + ostate->vtkglClearColor(0.0f, 0.0f, 0.0f, 1.0f); + ostate->vtkglViewport(0, 0, deviceSize.width(), deviceSize.height()); + f->glDisable(GL_SCISSOR_TEST); + f->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + f->glClear(GL_COLOR_BUFFER_BIT); +} + +//----------------------------------------------------------------------------- +void myQVTKOpenGLNativeWidget::initializeGL() +{ + this->Superclass::initializeGL(); + +#ifdef DEBUG_QVTKOPENGL_WIDGET + delete this->Logger; + this->Logger = new QOpenGLDebugLogger(this); + this->Logger->initialize(); // initializes in the current context. +#endif + + vtkmyQVTKOpenGLNativeWidgetDebugMacro("initializeGL"); + if (this->RenderWindow) + { + // use QSurfaceFormat for the widget, update ivars on the vtkRenderWindow. + myQVTKOpenGLNativeWidget::copyFromFormat(this->format(), this->RenderWindow); + // When a QOpenGLWidget is told to use a QSurfaceFormat with samples > 0, + // QOpenGLWidget doesn't actually create a context with multi-samples and + // internally changes the QSurfaceFormat to be samples=0. Thus, we can't + // rely on the QSurfaceFormat to indicate to us if multisampling is being + // used. We should use glGetRenderbufferParameteriv(..) to get + // GL_RENDERBUFFER_SAMPLES to determine the samples used. This is done by + // in recreateFBO(). + } + this->connect( + this->context(), SIGNAL(aboutToBeDestroyed()), SLOT(cleanupContext()), Qt::UniqueConnection); + this->requireRenderWindowInitialization(); +} + +//----------------------------------------------------------------------------- +void myQVTKOpenGLNativeWidget::requireRenderWindowInitialization() +{ + if (this->RenderWindow) + { + this->RenderWindow->SetReadyForRendering(false); + } +} + +//----------------------------------------------------------------------------- +void myQVTKOpenGLNativeWidget::resizeGL(int w, int h) +{ + vtkmyQVTKOpenGLNativeWidgetDebugMacro("resizeGL"); + this->Superclass::resizeGL(w, h); +} + +//----------------------------------------------------------------------------- +void myQVTKOpenGLNativeWidget::paintGL() +{ + if (this->InPaintGL) + { + return; + } + + if (!this->RenderWindow) + { + return; + } + + if (!this->FBO + || this->FBO->handle() != this->RenderWindow->GetDefaultFrameBufferId()) + { + this->recreateFBO(); + } + + QScopedValueRollback var(this->InPaintGL, true); + this->Superclass::paintGL(); + + if (this->DoVTKRenderInPaintGL && !this->renderVTK()) + { + vtkmyQVTKOpenGLNativeWidgetDebugMacro("paintGL:skipped-renderVTK"); + // This should be very rare, but it's conceivable that subclasses of + // myQVTKOpenGLNativeWidget are simply not ready to do a + // render on VTK render window when widget is being painted. + // Leave the buffer unchanged. + return; + } + + // We just did a render, if we needed it. Turn the flag off. + this->DoVTKRenderInPaintGL = false; + + // If render was triggered by above calls, that may change the current context + // due to things like progress events triggering updates on other widgets + // (e.g. progress bar). Hence we need to make sure to call makeCurrent() + // before proceeding with blit-ing. + this->makeCurrent(); + + // blit from this->FBO to QOpenGLWidget's FBO. + vtkmyQVTKOpenGLNativeWidgetDebugMacro("paintGL::blit-to-defaultFBO"); + QOpenGLFunctions_3_2_Core* f = + QOpenGLContext::currentContext()->versionFunctions(); + if (f) + { + vtkOpenGLState *ostate = this->RenderWindow->GetState(); + + f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, this->defaultFramebufferObject()); + f->glDrawBuffer(GL_COLOR_ATTACHMENT0); + + f->glBindFramebuffer(GL_READ_FRAMEBUFFER, this->FBO->handle()); + f->glReadBuffer(GL_COLOR_ATTACHMENT0); + + // The viewport state may be modified outside the vtkOpenGLState mechanism, so reset the state here. + ostate->ResetGlViewportState(); + + // If you get a vtkOpenGLState warning from the call below, you can add + // a call to ostate->ResetEnumState(GL_SCISSOR_TEST); to reset the cache state + // to whatever the value in OpenGL is. + ostate->vtkglDisable(GL_SCISSOR_TEST); // Scissor affects glBindFramebuffer. + f->glBlitFramebuffer(0, 0, this->RenderWindow->GetSize()[0], this->RenderWindow->GetSize()[1], + 0, 0, this->RenderWindow->GetSize()[0], this->RenderWindow->GetSize()[1], GL_COLOR_BUFFER_BIT, + GL_NEAREST); + + // now clear alpha otherwise we end up blending the rendering with + // background windows in certain cases. It happens on OsX + // (if QSurfaceFormat::alphaBufferSize() > 0) or when using Mesa on Linux + // (see paraview/paraview#17159). + GLboolean colorMask[4]; + f->glGetBooleanv(GL_COLOR_WRITEMASK, colorMask); + f->glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + + GLfloat clearColor[4]; + f->glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor); + f->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + f->glClear(GL_COLOR_BUFFER_BIT); + + f->glColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]); + f->glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + } +} + +//----------------------------------------------------------------------------- +void myQVTKOpenGLNativeWidget::cleanupContext() +{ + // logger gets uninitialized when this gets called. We get errors from + // QOpenGLDebugLogger if logMessage is called here. So we just destroy the + // logger. + delete this->Logger; + this->Logger = nullptr; + + vtkmyQVTKOpenGLNativeWidgetDebugMacro("cleanupContext"); + + // QOpenGLWidget says when this slot is called, the context may not be current + // and hence is a good practice to make it so. + this->makeCurrent(); + if (this->RenderWindow) + { + if (this->FBO) + { + this->FBO->bind(); + } + this->RenderWindow->Finalize(); + this->RenderWindow->SetReadyForRendering(false); + } + delete this->FBO; + this->FBO = nullptr; + this->requireRenderWindowInitialization(); +} + +//----------------------------------------------------------------------------- +void myQVTKOpenGLNativeWidget::windowFrameEventCallback() +{ + Q_ASSERT(this->RenderWindow); + vtkmyQVTKOpenGLNativeWidgetDebugMacro("frame"); + + if (!this->InPaintGL) + { + // Handing vtkOpenGLRenderWindow::Frame is tricky. VTK code traditionally + // calls `Frame` to indicate that VTK is done rendering 1 frame. Now, when + // that happens, should we tell Qt to update the widget -- that's the + // question? In general, yes, but sometimes VTK does things in the + // background i.e. back buffer without wanting to update the front buffer + // e.g. when making selections. In that case, we don't want to update Qt + // widget either, since whatever it did was not meant to be visible. + // To handle that, we check if vtkOpenGLRenderWindow::SwapBuffers is true, + // and request an update only when it is. + if (this->RenderWindow->GetSwapBuffers() || this->RenderWindow->GetDoubleBuffer() == 0) + { + // Means that the vtkRenderWindow rendered outside a paintGL call. That can + // happen when application code call vtkRenderWindow::Render() directly, + // instead of calling myQVTKOpenGLNativeWidget::update() or letting Qt update the + // widget. In that case, since QOpenGLWidget rendering into an offscreen + // FBO, the result still needs to be composed by Qt widget stack. We request + // that using `update()`. + vtkmyQVTKOpenGLNativeWidgetDebugMacro("update"); + this->update(); + + this->DoVTKRenderInPaintGL = false; + } + else + { + vtkmyQVTKOpenGLNativeWidgetDebugMacro("buffer bad -- do not show"); + + // Since this->FBO right now is garbage, if paint event is received before + // a Render request is made on the render window, we will have to Render + // explicitly. + this->DoVTKRenderInPaintGL = true; + } + } +} + +//----------------------------------------------------------------------------- +bool myQVTKOpenGLNativeWidget::renderVTK() +{ + vtkmyQVTKOpenGLNativeWidgetDebugMacro("renderVTK"); + Q_ASSERT(this->FBO); + Q_ASSERT(this->FBO->handle() == this->RenderWindow->GetDefaultFrameBufferId()); + + // Bind the FBO we'll be rendering into. This may not be needed, since VTK will + // bind it anyways, but we'll be extra cautious. + this->FBO->bind(); + + vtkRenderWindowInteractor* iren = this->RenderWindow ? this->RenderWindow->GetInteractor() : nullptr; + if (iren) + { + iren->Render(); + } + else if (this->RenderWindow) + { + this->RenderWindow->Render(); + } + else + { + // no render window set, just fill with white. + QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); + f->glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + f->glClear(GL_COLOR_BUFFER_BIT); + } + return true; +} + +//----------------------------------------------------------------------------- +bool myQVTKOpenGLNativeWidget::event(QEvent* evt) +{ + switch (evt->type()) + { + //case QEvent::MouseMove: + //case QEvent::MouseButtonPress: + //case QEvent::MouseButtonRelease: + //case QEvent::MouseButtonDblClick: + // skip events that are explicitly handled by overrides to avoid duplicate + // calls to InteractorAdapter->ProcessEvent(). + //break; + + //case QEvent::Resize: + case QEvent::Wheel: + // we don't let QVTKInteractorAdapter process resize since we handle it + // in this->recreateFBO(). + break; + +#ifdef __APPLE__ + // On MacOS, because of https://bugreports.qt.io/browse/QTBUG-69644 + // It is needed to hide/show currently visible QVTKOpenGLWidget + // Just after initialization of myQVTKOpenGLNativeWidget + // This triggers a timer so as soon as Qt is able to process events, + // it will fix the broken QVTKOpenGLWidgets. + case QEvent::WindowActivate: + case QEvent::PolishRequest: + { + QWidget* window = this->window(); + QTimer::singleShot(1, [window]() {::QVTKOpenGLWidgetMacOSCheck(window); }); + } + break; +#endif // __APPLE__ + + default: + if (this->RenderWindow && this->RenderWindow->GetInteractor()) + { + this->InteractorAdapter->ProcessEvent(evt, this->RenderWindow->GetInteractor()); + } + } + return this->Superclass::event(evt); +} + +//----------------------------------------------------------------------------- +//void myQVTKOpenGLNativeWidget::wheelEvent(QWheelEvent *event) +//{ +// +// +//} +//----------------------------------------------------------------------------- +//void myQVTKOpenGLNativeWidget::mouseMoveEvent(QMouseEvent* event) +//{ +// //emit mouseEvent(event); +// +// //if (this->RenderWindow && this->RenderWindow->GetInteractor()) +// //{ +// // this->InteractorAdapter->ProcessEvent(event, +// // this->RenderWindow->GetInteractor()); +// //} +//} + +//----------------------------------------------------------------------------- +//void myQVTKOpenGLNativeWidget::mouseReleaseEvent(QMouseEvent* event) +//{ +// //emit mouseEvent(event); +// +// //if (this->RenderWindow && this->RenderWindow->GetInteractor()) +// //{ +// // this->InteractorAdapter->ProcessEvent(event, +// // this->RenderWindow->GetInteractor()); +// //} +//} + +//----------------------------------------------------------------------------- +//void myQVTKOpenGLNativeWidget::mouseDoubleClickEvent(QMouseEvent* event) +//{ +// //emit mouseEvent(event); +// +// //if (this->RenderWindow && this->RenderWindow->GetInteractor()) +// //{ +// // this->InteractorAdapter->ProcessEvent(event, +// // this->RenderWindow->GetInteractor()); +// //} +//} + +//----------------------------------------------------------------------------- +void myQVTKOpenGLNativeWidget::cursorChangedCallback(vtkObject*, unsigned long, + void*, void* call_data) +{ + if (!this->RenderWindow) + { + return; + } + + int* cShape = reinterpret_cast (call_data); + if (!cShape) + { + return; + } + + switch (*cShape) + { + case VTK_CURSOR_CROSSHAIR: + this->setCursor(QCursor(Qt::CrossCursor)); + break; + case VTK_CURSOR_SIZEALL: + this->setCursor(QCursor(Qt::SizeAllCursor)); + break; + case VTK_CURSOR_SIZENS: + this->setCursor(QCursor(Qt::SizeVerCursor)); + break; + case VTK_CURSOR_SIZEWE: + this->setCursor(QCursor(Qt::SizeHorCursor)); + break; + case VTK_CURSOR_SIZENE: + this->setCursor(QCursor(Qt::SizeBDiagCursor)); + break; + case VTK_CURSOR_SIZENW: + this->setCursor(QCursor(Qt::SizeFDiagCursor)); + break; + case VTK_CURSOR_SIZESE: + this->setCursor(QCursor(Qt::SizeFDiagCursor)); + break; + case VTK_CURSOR_SIZESW: + this->setCursor(QCursor(Qt::SizeBDiagCursor)); + break; + case VTK_CURSOR_HAND: + this->setCursor(QCursor(Qt::PointingHandCursor)); + break; + case VTK_CURSOR_ARROW: + default: + this->setCursor(QCursor(Qt::ArrowCursor)); + break; + } +} diff --git a/src/src/view/subview/DicomTreeModel.cpp b/src/src/view/subview/DicomTreeModel.cpp new file mode 100644 index 0000000..789511c --- /dev/null +++ b/src/src/view/subview/DicomTreeModel.cpp @@ -0,0 +1,106 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +#include "DicomTreeModel.h" +#include + + + +#include "include_dcmtk.h" + + // Maximal number of symbols to be used to display large tags data +const int TagPreviewLength = 128; + +template +class NewDcmItem : public T { +public: + NewDcmItem(const T &old) : T(old) { + } + DcmList *GetDcmList()const { + return this->elementList; + } +protected: + virtual ~NewDcmItem() {} +}; +using MyDcmDataset = NewDcmItem; +using MyDcmMetaInfo = NewDcmItem; +using MywDcmItem = NewDcmItem; + + +DicomTreeModel::DicomTreeModel(DcmFileFormat *file, QObject *parent) + : QStandardItemModel(parent) +{ + MyDcmMetaInfo *meta_info = new MyDcmMetaInfo(*file->getMetaInfo()); + MyDcmDataset *dataset_info = new MyDcmDataset(*file->getDataset()); + attachTagList(invisibleRootItem(), meta_info); + attachTagList(invisibleRootItem(), dataset_info); +} + +QVariant DicomTreeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) { + return QVariant(); + } + + if (orientation == Qt::Horizontal) { + switch (section) { + case 0: + return tr("Tag ID"); + case 1: + return tr("VR"); + case 2: + return tr("VM"); + case 3: + return tr("Length"); + case 4: + return tr("Description"); + case 5: + return tr("Value"); + default: + return QVariant(); + } + } + + return QVariant(); +} + + +template +void DicomTreeModel::attachTagList(QStandardItem *item, T *dataset) +//void DicomTreeModel::attachTagList(QStandardItem *item, MyDcmDataset *dataset) +{ + Q_ASSERT(item); + DcmList *tagList = dataset->GetDcmList(); + Q_ASSERT(tagList); + + if (!tagList->empty()) { + DcmObject *dO; + DcmTag tag; + OFString value; + tagList->seek(ELP_first); + do { + dO = tagList->get(); + tag = dO->getTag(); + DcmElement *elem; + dataset->findAndGetElement(tag, elem); + elem->getOFString(value, 0); + + QList row; + row << new QStandardItem(QString::fromStdString(tag.toString())); + row << new QStandardItem(tag.getVRName()); + row << new QStandardItem(QString::number(dO->getVM())); + row << new QStandardItem(QString::number(dO->getLength())); + row << new QStandardItem(tag.getTagName()); + row << new QStandardItem(QString::fromStdString(value)); + item->appendRow(row); + } while (tagList->seek(ELP_next)); + delete dO; + } +} diff --git a/src/src/view/subview/customwindow.cpp b/src/src/view/subview/customwindow.cpp new file mode 100644 index 0000000..706eaa4 --- /dev/null +++ b/src/src/view/subview/customwindow.cpp @@ -0,0 +1,43 @@ +#include "view/subview/customwindow.h" +#include "view/dicomimageview.h" + +Customwindow::Customwindow(QWidget *parent) + : QDialog(parent) +{ + ui.setupUi(this); + + connect(ui.pBtn_OK, SIGNAL(clicked()), this, SLOT(onBtnOKClicked())); + connect(ui.PBtn_Cancel, SIGNAL(clicked()), this, SLOT(onBtnCancelClicked())); +} + +Customwindow::~Customwindow() +{ + +} + +void Customwindow::onBtnCancelClicked() +{ + reject(); +} + + +void Customwindow::setCurrentView(DicomImageView *cur) +{ + m_curV = cur; + if (nullptr == m_curV) + { + return; + } + double width = 0; + double level = 0; + m_curV->getWindowLevel(level, width); + ui.led_WL->setText(QString("%1").arg(level)); + ui.led_WW->setText(QString("%1").arg(width)); +} + + +void Customwindow::onBtnOKClicked() +{ + m_curV->setWindowLevel(ui.led_WL->text().toDouble(), ui.led_WW->text().toDouble()); + this->close(); +} \ No newline at end of file diff --git a/src/src/view/subview/gridpopwidget.cpp b/src/src/view/subview/gridpopwidget.cpp new file mode 100644 index 0000000..71cd9b6 --- /dev/null +++ b/src/src/view/subview/gridpopwidget.cpp @@ -0,0 +1,70 @@ +锘#include "view/subview/gridpopwidget.h" +#include "ui_gridpopwidget.h" + +//#include +//#include +#include +#define NORMAL_STYLE "background-color: rgb(128, 128, 128);" +#define HIGHLIGHT_STYLE "background-color: rgb(200, 200, 200);" + +//------------------------------------------------------- +GridPopWidget::GridPopWidget(QWidget *parent) : + QWidget(parent), + ui(new Ui::GridPopWidget) { + ui->setupUi(this); + setWindowFlags(windowFlags() | Qt::Popup | Qt::FramelessWindowHint); + setMouseTracking(true); + wArr << (QList() << ui->w11 << ui->w12 << ui->w13 << ui->w14 << ui->w15); + wArr << (QList() << ui->w21 << ui->w22 << ui->w23 << ui->w24 << ui->w25); + wArr << (QList() << ui->w31 << ui->w32 << ui->w33 << ui->w34 << ui->w35); + wArr << (QList() << ui->w41 << ui->w42 << ui->w43 << ui->w44 << ui->w45); +} + +//------------------------------------------------------- +GridPopWidget::~GridPopWidget() { + delete ui; +} + +//------------------------------------------------------- +void GridPopWidget::mousePressEvent(QMouseEvent *e) { + QWidget::mousePressEvent(e); + QPoint p = e->pos(); + int row = 0, col = 0; + QPoint lim = rect().bottomRight(); + if (p.x() > lim.x() || p.y() > lim.y()) { + return; + } + foreach (QWidget *w, wArr.first()) { + if (p.x() > w->pos().x()) { + col++; + } + } + foreach (QList ws, wArr) { + if (p.y() > ws.first()->pos().y()) { + row++; + } + } + emit Signal_ViewLayout(col, row); + close(); +} + +//------------------------------------------------------- +void GridPopWidget::mouseMoveEvent(QMouseEvent *e) { + QWidget::mouseMoveEvent(e); + QPoint p = e->pos(); + foreach (QList ws, wArr) { + foreach (QWidget *w, ws) { + if (p.x() > w->pos().x() && p.y() > w->pos().y()) { + w->setStyleSheet(HIGHLIGHT_STYLE); + } else { + w->setStyleSheet(NORMAL_STYLE); + } + } + } +} + +//------------------------------------------------------- +void GridPopWidget::hideEvent(QHideEvent *e) { + QWidget::hideEvent(e); + deleteLater(); +} diff --git a/src/src/view/subview/importtitlebar.cpp b/src/src/view/subview/importtitlebar.cpp new file mode 100644 index 0000000..174fe4a --- /dev/null +++ b/src/src/view/subview/importtitlebar.cpp @@ -0,0 +1,117 @@ +#include "view/subview/importtitlebar.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ImportTitleBar::ImportTitleBar(QWidget *parent) + : QWidget(parent) + , m_pButtonGroup(nullptr) + , m_pMainLayout(nullptr) + , m_pLogoLabel(nullptr) + , m_pTitleLabel(nullptr) + , m_pSpacerItem(nullptr) + , m_pCloseButton(nullptr) + , m_pMinimumButton(nullptr) + , m_pMaximumButton(nullptr) +{ + initUi(); + initSys(); +} + +ImportTitleBar::~ImportTitleBar() +{ +} + +void ImportTitleBar::setTitleText(const QString& title) +{ + m_pTitleLabel->setText(title); +} + +void ImportTitleBar::initUi() +{ + + m_pMainLayout = new QHBoxLayout(this); + m_pMainLayout->setContentsMargins(0, 0, 0, 0); + + m_pLogoLabel = new QLabel(this); + QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(m_pLogoLabel->sizePolicy().hasHeightForWidth()); + m_pLogoLabel->setSizePolicy(sizePolicy); + m_pLogoLabel->setBaseSize(30, 30); + m_pLogoLabel->setPixmap(QPixmap(QString::fromUtf8(":/importwidget/Resources/import/icon.png"))); + m_pLogoLabel->setScaledContents(true); + m_pMainLayout->addWidget(m_pLogoLabel); + + m_pTitleLabel = new QLabel(this); + m_pTitleLabel->setText(tr("Search and downlod studies from PACS locations")); + m_pMainLayout->addWidget(m_pTitleLabel); + + m_pSpacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + m_pMainLayout->addItem(m_pSpacerItem); + + m_pButtonGroup = new QButtonGroup(this); + m_pMinimumButton = new QPushButton(this); + QIcon minIcon; + minIcon.addPixmap(QPixmap(QString::fromUtf8(":/importwidget/Resources/import/min.png"))); + m_pMinimumButton->setIcon(minIcon); + m_pMinimumButton->setIconSize(QSize(22, 22)); + m_pMinimumButton->setStyleSheet("QPushButton{background:rgba(0,0,0,0);border:1px solid rgba(0,0,0,0);}"); + m_pButtonGroup->addButton(m_pMinimumButton, 1); + m_pMainLayout->addWidget(m_pMinimumButton); + + m_pMaximumButton = new QPushButton(this); + QIcon maxIcon; + maxIcon.addPixmap(QPixmap(QString::fromUtf8(":/importwidget/Resources/import/max.png"))); + m_pMaximumButton->setIcon(maxIcon); + m_pMaximumButton->setIconSize(QSize(22, 22)); + m_pMaximumButton->setStyleSheet("QPushButton{background:rgba(0,0,0,0);border:1px solid rgba(0,0,0,0);}"); + m_pButtonGroup->addButton(m_pMaximumButton, 3); + m_pMainLayout->addWidget(m_pMaximumButton); + + m_pCloseButton = new QPushButton(this); + QIcon closeIcon; + closeIcon.addPixmap(QPixmap(QString::fromUtf8(":/importwidget/Resources/import/close.png"))); + m_pCloseButton->setIcon(closeIcon); + m_pCloseButton->setIconSize(QSize(22, 22)); + m_pCloseButton->setStyleSheet("QPushButton{background:rgba(0,0,0,0);border:1px solid rgba(0,0,0,0);}"); + m_pButtonGroup->addButton(m_pCloseButton, 2); + m_pMainLayout->addWidget(m_pCloseButton); +} + +void ImportTitleBar::initSys() +{ + connect(m_pButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(onButtonClicked(int))); +} + +void ImportTitleBar::onButtonClicked(int index) +{ + switch (index) + { + case 2: + emit sigClose(); + break; + case 1: + emit sigShowMinimum(); + break; + case 3: + emit sigShowMaximum(); + break; + default: + break; + } +} + + + + + + + diff --git a/src/src/view/subview/importwidget.cpp b/src/src/view/subview/importwidget.cpp new file mode 100644 index 0000000..69291bc --- /dev/null +++ b/src/src/view/subview/importwidget.cpp @@ -0,0 +1,783 @@ +#include "view/subview/importwidget.h" +#include "view/subview/importtitlebar.h" +#include "view/subview/pacsconfiguration.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dialog/promptdialog.h" +#include "callback/cfindcallback.h" +#include "callback/callbackhelper.h" +#include "base/dicomviewerhelper.h" +#include "dcmtk/dcmdata/dcdatset.h" +#include "dcm_move.h" +#include "callback/cmovecallback.h" +#include "callback/cmovestorescpcallback.h" + +ImportWidget::ImportWidget(QWidget *parent) + : QDialog(parent) + , m_pMainLayout(nullptr) + , m_pTitleBar(nullptr) + , m_pStudyModel(nullptr) + //, m_pStudySelectionModel(nullptr) + , m_pSeriesModel(nullptr) + //, m_pSeriesSelectionModel(nullptr) + , m_pPacsComboBox(nullptr) + , m_pModalityComboBox(nullptr) + , m_pStartDate(nullptr) + , m_pEndDate(nullptr) + , m_pDicomComboBox(nullptr) + , m_pDicomEdit(nullptr) + , m_pSearchButton(nullptr) + , m_pClearButton(nullptr) + , m_pDateComboBox(nullptr) + , m_pStudyResult(nullptr) + , m_pSeriesResult(nullptr) + , m_pSettingDialog(nullptr) + , m_pMsgBox(nullptr) + , m_CallbackHelper(nullptr) + , m_pProgressWidget(nullptr) + , m_pProgressLayout(nullptr) + , m_pProgressItem(nullptr) + , m_pProgressBar(nullptr) + , m_pQueryWorkerThread(nullptr) + , m_pQueryWorker(nullptr) + , m_pMoveWorkerThread(nullptr) + , m_pMoveWorker(nullptr) +{ + qRegisterMetaType("PACSStudyInfo"); + qRegisterMetaType("PACSSeriesInfo"); + initUi(); + initSys(); +} + +ImportWidget::~ImportWidget() +{ + if (m_pQueryWorkerThread != nullptr && !m_pQueryWorkerThread->isFinished()) + { + m_pQueryWorkerThread->exit(); + m_pQueryWorkerThread->wait(); + delete m_pQueryWorkerThread; + m_pQueryWorkerThread = nullptr; + } + + if (m_pQueryWorker != nullptr) + { + delete m_pQueryWorker; + m_pQueryWorker = nullptr; + } + + if (m_pMoveWorkerThread != nullptr && !m_pMoveWorkerThread->isFinished()) + { + m_pMoveWorkerThread->exit(); + m_pMoveWorkerThread->wait(); + delete m_pMoveWorkerThread; + m_pMoveWorkerThread = nullptr; + } + + if (m_pMoveWorker != nullptr) + { + delete m_pMoveWorker; + m_pMoveWorker = nullptr; + } + +} + +void ImportWidget::initUi() +{ + //this->setWindowFlags(Qt::FramelessWindowHint); + this->setWindowTitle("PACS Configuration"); + this->resize(780, 470); + this->setMinimumSize(QSize(780, 470)); + + m_pMainLayout = new QVBoxLayout(this); + m_pMainLayout->setContentsMargins(10, 0, 10, 30); + m_pTitleBar = new ImportTitleBar(this); + m_pTitleBar->setFixedHeight(40); + m_pMainLayout->addWidget(m_pTitleBar); + + m_pFilterWidget = new QWidget(this); + m_pFilterLayout = new QGridLayout(m_pFilterWidget); + m_pSettingButton = new QPushButton(m_pFilterWidget); + QIcon setIcon; + setIcon.addPixmap(QPixmap(QString::fromUtf8(":/importwidget/Resources/import/setting.png"))); + m_pSettingButton->setIcon(setIcon); + m_pSettingButton->setIconSize(QSize(22, 22)); + //m_pSettingButton->setStyleSheet("QPushButton{background:rgba(0,0,0,0);border:1px solid rgba(0,0,0,0);}"); + m_pFilterLayout->addWidget(m_pSettingButton, 0, 0); + m_pPacsComboBox = new QComboBox(m_pFilterWidget); + m_pFilterLayout->addWidget(m_pPacsComboBox, 0, 1, 1, 4); + m_pModalityComboBox = new QComboBox(m_pFilterWidget); + m_pFilterLayout->addWidget(m_pModalityComboBox, 0, 5, 1, 3); + m_pDateComboBox = new QComboBox(m_pFilterWidget); + m_pFilterLayout->addWidget(m_pDateComboBox, 0, 8, 1, 3); + m_pStartDate = new QDateEdit(m_pFilterWidget); + m_pFilterLayout->addWidget(m_pStartDate, 0, 11, 1, 2); + m_pEndDate = new QDateEdit(m_pFilterWidget); + m_pFilterLayout->addWidget(m_pEndDate, 0, 13, 1, 2); + + m_pDicomComboBox = new QComboBox(m_pFilterWidget); + m_pFilterLayout->addWidget(m_pDicomComboBox, 1, 0, 1, 5); + m_pDicomEdit = new QLineEdit(m_pFilterWidget); + m_pFilterLayout->addWidget(m_pDicomEdit, 1, 5, 1, 6); + m_pSearchButton = new QPushButton(m_pFilterWidget); + m_pSearchButton->setText(tr("Search")); + m_pFilterLayout->addWidget(m_pSearchButton, 1, 11, 1, 2); + m_pClearButton = new QPushButton(m_pFilterWidget); + m_pClearButton->setText(tr("Clear")); + m_pFilterLayout->addWidget(m_pClearButton, 1, 13, 1, 2); + //m_pMainLayout->addLayout(m_pFilterLayout); + m_pMainLayout->addWidget(m_pFilterWidget); + + + m_pStudyResult = new QTableView(this); + m_pMainLayout->addWidget(m_pStudyResult); + m_pSeriesResult = new QTableView(this); + m_pMainLayout->addWidget(m_pSeriesResult); + + + m_pProgressWidget = new QWidget(this); + m_pProgressLayout = new QHBoxLayout(m_pProgressWidget); + m_pProgressLayout->setAlignment(Qt::AlignRight); + m_pProgressItem = new QSpacerItem(200, 10, QSizePolicy::Expanding, QSizePolicy::Expanding); + m_pProgressLayout->addItem(m_pProgressItem); + m_pProgressBar = new QProgressBar(m_pProgressWidget); + m_pProgressLayout->addWidget(m_pProgressBar); + m_pMainLayout->addWidget(m_pProgressWidget); + //m_pProgressWidget->setVisible(false); + + initFilterPacs(); + initFilterModality(); + initFilterDate(); + initFilterDicom(); + initStudy(); + initSeries(); +} + +void ImportWidget::initSys() +{ + m_pTitleBar->installEventFilter(this); + connect(m_pTitleBar, SIGNAL(sigClose()), this, SLOT(close())); + connect(m_pTitleBar, SIGNAL(sigShowMinimum()), this, SLOT(showMinumum())); + connect(m_pTitleBar, SIGNAL(sigShowMaximum()), this, SLOT(showMaximum())); + connect(m_pSettingButton, SIGNAL(clicked()), this, SLOT(configure())); + connect(m_pSearchButton, SIGNAL(clicked()), this, SLOT(query())); + connect(m_pClearButton, SIGNAL(clicked()), this, SLOT(clear())); + connect(m_pStudyResult, SIGNAL(clicked(const QModelIndex &)), this, SLOT(onStudySelected(const QModelIndex &))); + connect(m_pSeriesResult, SIGNAL(clicked(const QModelIndex &)), this, SLOT(onSeriesSelected(const QModelIndex &))); + connect(m_pSeriesResult, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(onSeriesDoubleClicked(const QModelIndex &))); + connect(m_pDateComboBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(onDateFilterChanged(const QString &))); + +} + +void ImportWidget::initFilterPacs() +{ + m_lHosts.clear(); + DicomViewerHelper::pacsInfo(m_lHosts); + + m_pPacsComboBox->clear(); + + QList::const_iterator itr = m_lHosts.begin(); + for (; itr != m_lHosts.end(); ++itr) + { + m_pPacsComboBox->addItem(itr->name); + } + + //for each (host var in m_lHosts) + //{ + // m_pPacsComboBox->addItem(var.name); + //} +} + +void ImportWidget::initFilterModality() +{ + m_pModalityComboBox->addItem("All modalities"); + m_pModalityComboBox->addItem("CT"); + m_pModalityComboBox->addItem("US"); + m_pModalityComboBox->addItem("MR"); + m_pModalityComboBox->setEnabled(false); +} + + +void ImportWidget::initFilterDate() +{ + m_pDateComboBox->addItem("All dates"); + m_pDateComboBox->addItem("-----------"); + m_pDateComboBox->addItem("Today"); + m_pDateComboBox->addItem("Yesterday"); + m_pDateComboBox->addItem("This week"); + m_pDateComboBox->addItem("This month"); + m_pDateComboBox->addItem("Custom date"); + m_pDateComboBox->addItem("Custom date range"); + m_pStartDate->setEnabled(false); + m_pEndDate->setEnabled(false); +} + +void ImportWidget::initFilterDicom() +{ + m_pDicomComboBox->addItem("Patient name"); + m_pDicomComboBox->addItem("Patient ID"); + m_pDicomComboBox->addItem("Accession number"); + //m_pDicomComboBox->addItem("Study description"); + //m_pDicomComboBox->addItem("Referring physician"); + //m_pDicomComboBox->addItem("Performing physician"); + //m_pDicomComboBox->addItem("Reading physician"); + //m_pDicomComboBox->addItem("Institution name"); +} + +void ImportWidget::initStudyHeader() +{ + m_lStudyHeaders.clear(); + m_lStudyHeaders << tr("Study date") << tr("Patient name") << tr("Date of birth") << tr("Patient ID") + << tr("Modality") << tr("Study description") << tr("Accession number") << tr("Exam ID") + << tr("Referring physician") << tr("Performing physician") << tr("Reading physician") << tr("Institution name") + << tr("Images") << tr("Source") << tr("uid"); +} + +void ImportWidget::initStudy() +{ + initStudyHeader(); + m_pStudyModel = new QStandardItemModel; + m_pStudyModel->setHorizontalHeaderLabels(m_lStudyHeaders); + //m_pStudySelectionModel = new QItemSelectionModel; + //m_pStudyResult->setSelectionModel(m_pStudySelectionModel); + m_pStudyResult->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + m_pStudyResult->verticalHeader()->setVisible(false); + m_pStudyResult->setShowGrid(false); + m_pStudyResult->setSelectionMode(QAbstractItemView::SingleSelection); + m_pStudyResult->setSelectionBehavior(QAbstractItemView::SelectRows); + m_pStudyResult->setModel(m_pStudyModel); +} + +void ImportWidget::initSeriesHeader() +{ + m_lSeriesHeaders.clear(); + m_lSeriesHeaders << tr("#") << tr("Number") << tr("Modality") << tr("Description") << tr("#Images"); +} + +void ImportWidget::initSeries() +{ + initSeriesHeader(); + m_pSeriesModel = new QStandardItemModel; + m_pSeriesModel->setHorizontalHeaderLabels(m_lSeriesHeaders); + //m_pSeriesSelectionModel = new QItemSelectionModel; + //m_pSeriesResult->setSelectionModel(m_pSeriesSelectionModel); + m_pSeriesResult->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); + m_pSeriesResult->verticalHeader()->setVisible(false); + m_pSeriesResult->setShowGrid(false); + m_pSeriesResult->setSelectionMode(QAbstractItemView::SingleSelection); + m_pSeriesResult->setSelectionBehavior(QAbstractItemView::SelectRows); + m_pSeriesResult->setModel(m_pSeriesModel); +} + +bool ImportWidget::eventFilter(QObject *obj, QEvent *event) +{ + if (m_pTitleBar == obj) + { + if (event->type() == QEvent::MouseButtonPress) + { + QRect dragArea = m_pTitleBar->geometry(); + if (dragArea.contains(mapFromGlobal(QCursor::pos())) && !this->isMaximized()) + { + m_dragState.dragging = true; + m_dragState.dragStartPosition = QCursor::pos(); + } + } + else if (event->type() == QEvent::MouseButtonRelease) + { + m_dragState.dragging = false; + } + else if (m_dragState.dragging && event->type() == QEvent::MouseMove) + { + const QPoint& curPos = QCursor::pos(); + this->move(this->geometry().topLeft() + (curPos - m_dragState.dragStartPosition)); + m_dragState.dragStartPosition = curPos; + } + } + return QDialog::eventFilter(obj, event); +} + +void ImportWidget::close() +{ + reject(); +} + +void ImportWidget::showMaximum() +{ + isMaximized() ? showNormal() : showMaximized(); +} + +void ImportWidget::showMinumum() +{ + showMinimized(); +} + +void ImportWidget::getNetParams(std::string& peerIP, unsigned long& peerPort, std::string& peerTitle, unsigned long& ourPort, std::string& ourTitle) +{ + ourPort = DicomViewerHelper::ourPort().toULong(); + ourTitle = DicomViewerHelper::ourTitle().toStdString(); + + QList hosts; + DicomViewerHelper::pacsInfo(hosts); + + QList::ConstIterator itr = hosts.begin(); + for (; itr != hosts.end(); ++itr) + { + if (itr->name == m_pPacsComboBox->currentText()) + { + peerIP = itr->ip.toStdString(); + peerPort = itr->port.toULong(); + peerTitle = itr->ae.toStdString(); + break; + } + } + //for each (host var in hosts) + //{ + // if (var.name == m_pPacsComboBox->currentText()) + // { + // peerIP = var.ip.toStdString(); + // peerPort = var.port.toULong(); + // peerTitle = var.ae.toStdString(); + // break; + // } + //} +} + +void ImportWidget::onQueryDone(int code) +{ + updateStudyView(); + updateSeriesView(); +} + +void ImportWidget::getDateIntervalForQuery(QString& startDate, QString& endDate) +{ + startDate.clear(); endDate.clear(); + + QDate startDate_, endDate_, date = QDate::currentDate(); + if (m_pDateComboBox->currentText() == "Today") + { + startDate_ = date; + } + else if(m_pDateComboBox->currentText() == "Yesterday") + { + startDate_ = date.addDays(-1); + } + else if (m_pDateComboBox->currentText() == "This week") + { + startDate_ = date.addDays(-date.dayOfWeek() + 1); + } + else if (m_pDateComboBox->currentText() == "This month") + { + startDate_ = QDate(date.year(), date.month(), 1); + } + else if (m_pDateComboBox->currentText() == "Custom date") + { + startDate_ = m_pStartDate->date(); + } + else if (m_pDateComboBox->currentText() == "Custom date range") + { + startDate_ = m_pStartDate->date(); + endDate_ = m_pEndDate->date(); + } + else + { + startDate_ = date; + } + + startDate = QString::number(startDate_.year()); + if (startDate_.month() < 10) + { + startDate += "0"; + } + startDate += QString::number(startDate_.month()); + if (startDate_.day() < 10) + { + startDate += "0"; + } + startDate += QString::number(startDate_.day()); + + if (endDate_.isNull()) + return; + endDate = QString::number(endDate_.year()); + if (endDate_.month() < 10) + { + endDate += "0"; + } + endDate += QString::number(endDate_.month()); + if (endDate_.day() < 10) + { + endDate += "0"; + } + endDate += QString::number(endDate_.day()); +} + +// if pacs info changed, need reset the info +void ImportWidget::query() +{ + if (m_pQueryWorkerThread == nullptr) + { + m_pQueryWorkerThread = new QThread; + } + else + { + if (!m_pQueryWorkerThread->isFinished()) + { + m_pQueryWorkerThread->quit(); + m_pQueryWorkerThread->wait(); + } + } + + std::string peerIP, peerTitle, ourTitle; + unsigned long peerPort = 0, ourPort = 0; + getNetParams(peerIP, peerPort, peerTitle, ourPort, ourTitle); + if (peerIP.empty() || peerTitle.empty() || ourTitle.empty() || peerPort == 0 || ourPort == 0) + { + return; + } + + if (m_pQueryWorker == nullptr) + { + m_pQueryWorker = new QueryWorker(QString::fromStdString(peerIP), peerPort, QString::fromStdString(peerTitle), QString::fromStdString(ourTitle)); + QObject::connect(m_pQueryWorker, SIGNAL(sendFindDone(int)), this, SLOT(onQueryDone(int)), Qt::QueuedConnection); + QObject::connect(m_pQueryWorker, SIGNAL(sendStudyItemFound(PACSStudyInfo)), this, SLOT(onStudyFoundResult(PACSStudyInfo)), Qt::QueuedConnection); + QObject::connect(m_pQueryWorker, SIGNAL(sendSeriesItemFound(PACSSeriesInfo)), this, SLOT(onSeriesFoundResult(PACSSeriesInfo)), Qt::QueuedConnection); + } + m_pQueryWorker->setPacsInfo(QString::fromStdString(peerIP), peerPort, QString::fromStdString(peerTitle), QString::fromStdString(ourTitle)); + m_pQueryWorker->moveToThread(m_pQueryWorkerThread); + m_pQueryWorkerThread->start(); + + m_studyInfo.clear(); + + QString studyStartDate, studyEndDate; + getDateIntervalForQuery(studyStartDate, studyEndDate); + if (m_pDicomComboBox->currentText() == "Patient ID") + { + if(m_pDateComboBox->currentText() == "All dates" || m_pDateComboBox->currentText() == "-----------") + QMetaObject::invokeMethod(m_pQueryWorker, "queryByPatientID", Qt::QueuedConnection, Q_ARG(QString, m_pDicomEdit->text())); + else + QMetaObject::invokeMethod(m_pQueryWorker, "queryByPatientIDAndDate", Qt::QueuedConnection, Q_ARG(QString, m_pDicomEdit->text()), Q_ARG(QString, studyStartDate), Q_ARG(QString, studyEndDate)); + } + else if (m_pDicomComboBox->currentText() == "Patient name") + { + if (m_pDateComboBox->currentText() == "All dates" || m_pDateComboBox->currentText() == "-----------") + QMetaObject::invokeMethod(m_pQueryWorker, "queryByPatientName", Qt::QueuedConnection, Q_ARG(QString, m_pDicomEdit->text())); + else + QMetaObject::invokeMethod(m_pQueryWorker, "queryByPatientNameAndDate", Qt::QueuedConnection, Q_ARG(QString, m_pDicomEdit->text()), Q_ARG(QString, studyStartDate), Q_ARG(QString, studyEndDate)); + } + else if (m_pDicomComboBox->currentText() == "Accession number") + { + if (m_pDateComboBox->currentText() == "All dates" || m_pDateComboBox->currentText() == "-----------") + QMetaObject::invokeMethod(m_pQueryWorker, "queryByAccessNo", Qt::QueuedConnection, Q_ARG(QString, m_pDicomEdit->text())); + else + QMetaObject::invokeMethod(m_pQueryWorker, "queryByAccessNoAndDate", Qt::QueuedConnection, Q_ARG(QString, m_pDicomEdit->text()), Q_ARG(QString, studyStartDate), Q_ARG(QString, studyEndDate)); + } +} + +void ImportWidget::onStudyFoundResult(PACSStudyInfo studyInfo) +{ + m_studyInfo.push_back(studyInfo); + updateStudyView(); +} + +void ImportWidget::updateStudyView() +{ + m_pStudyModel->clear(); + m_pStudyModel->setHorizontalHeaderLabels(m_lStudyHeaders); + int iRowIdx = 0; + + QList::ConstIterator itr = m_studyInfo.begin(); + for (; itr != m_studyInfo.end(); ++itr) + { + QString n = itr->studyDate; + QStandardItem* date = new QStandardItem(itr->studyDate); date->setEditable(false); + QStandardItem* name = new QStandardItem(itr->patientName); name->setEditable(false); + QStandardItem* birth = new QStandardItem(itr->patientBirthDate); birth->setEditable(false); + QStandardItem* id = new QStandardItem(itr->patientID); id->setEditable(false); + QStandardItem* modality = new QStandardItem(""); modality->setEditable(false); + QStandardItem* description = new QStandardItem(""); description->setEditable(false); + QStandardItem* accNo = new QStandardItem(itr->accessionNumber); accNo->setEditable(false); + QStandardItem* examId = new QStandardItem(""); examId->setEditable(false); + QStandardItem* referPhy = new QStandardItem(""); referPhy->setEditable(false); + QStandardItem* performPhy = new QStandardItem(""); performPhy->setEditable(false); + QStandardItem* readingPhysician = new QStandardItem(itr->requestingPhysician); readingPhysician->setEditable(false); + QStandardItem* inst = new QStandardItem(""); inst->setEditable(false); + QStandardItem* image = new QStandardItem(""); image->setEditable(false); + QStandardItem* source = new QStandardItem(m_pPacsComboBox->currentText()); source->setEditable(false); + QStandardItem* uid = new QStandardItem(itr->studyInstanceUID); uid->setEditable(false); + QList list; + list << date << name << birth << id << modality << description << accNo << examId << referPhy + << performPhy << readingPhysician << inst << image << source << uid; + m_pStudyModel->insertRow(iRowIdx, list); + ++iRowIdx; + } + + //for each (PACSStudyInfo var in m_studyInfo) + //{ + // QString n = var.studyDate; + // QStandardItem* date = new QStandardItem(var.studyDate); date->setEditable(false); + // QStandardItem* name = new QStandardItem(var.patientName); name->setEditable(false); + // QStandardItem* birth = new QStandardItem(var.patientBirthDate); birth->setEditable(false); + // QStandardItem* id = new QStandardItem(var.patientID); id->setEditable(false); + // QStandardItem* modality = new QStandardItem(""); modality->setEditable(false); + // QStandardItem* description = new QStandardItem(""); description->setEditable(false); + // QStandardItem* accNo = new QStandardItem(var.accessionNumber); accNo->setEditable(false); + // QStandardItem* examId = new QStandardItem(""); examId->setEditable(false); + // QStandardItem* referPhy = new QStandardItem(""); referPhy->setEditable(false); + // QStandardItem* performPhy = new QStandardItem(""); performPhy->setEditable(false); + // QStandardItem* readingPhysician = new QStandardItem(var.requestingPhysician); readingPhysician->setEditable(false); + // QStandardItem* inst = new QStandardItem(""); inst->setEditable(false); + // QStandardItem* image = new QStandardItem(""); image->setEditable(false); + // QStandardItem* source = new QStandardItem(m_pPacsComboBox->currentText()); source->setEditable(false); + // QStandardItem* uid = new QStandardItem(var.studyInstanceUID); uid->setEditable(false); + // QList list; + // list << date << name << birth << id << modality << description << accNo << examId << referPhy + // << performPhy << readingPhysician << inst << image << source << uid; + // m_pStudyModel->insertRow(iRowIdx, list); + // ++iRowIdx; + //} + m_pStudyResult->setModel(m_pStudyModel); + m_pStudyResult->setColumnHidden(14, true); +} + + +void ImportWidget::onStudySelected(const QModelIndex ¤t) +{ + int row = current.row(); + if (row < 0) + return; + + if (m_pQueryWorkerThread == nullptr) + { + m_pQueryWorkerThread = new QThread; + } + else + { + if (!m_pQueryWorkerThread->isFinished()) + { + m_pQueryWorkerThread->exit(); + m_pQueryWorkerThread->wait(); + } + } + + if (m_pQueryWorker == nullptr) + { + std::string peerIP, peerTitle, ourTitle; + unsigned long peerPort = 0, ourPort = 0; + getNetParams(peerIP, peerPort, peerTitle, ourPort, ourTitle); + if (peerIP.empty() || peerTitle.empty() || ourTitle.empty() || peerPort == 0 || ourPort == 0) + { + return; + } + + m_pQueryWorker = new QueryWorker(QString::fromStdString(peerIP), peerPort, QString::fromStdString(peerTitle), QString::fromStdString(ourTitle)); + QObject::connect(m_pQueryWorker, SIGNAL(sendFindDone(int)), this, SLOT(onQueryDone(int)), Qt::QueuedConnection); + QObject::connect(m_pQueryWorker, SIGNAL(sendStudyItemFound(PACSStudyInfo)), this, SLOT(onStudyFoundResult(PACSStudyInfo)), Qt::QueuedConnection); + QObject::connect(m_pQueryWorker, SIGNAL(sendSeriesItemFound(PACSSeriesInfo)), this, SLOT(onSeriesFoundResult(PACSSeriesInfo)), Qt::QueuedConnection); + } + m_pQueryWorker->moveToThread(m_pQueryWorkerThread); + m_pQueryWorkerThread->start(); + + m_seriesInfo.clear(); + std::string studyInstanceUID = m_pStudyModel->index(row, 14).data().toString().toStdString(); + QMetaObject::invokeMethod(m_pQueryWorker, "queryBySeriesUID", Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(studyInstanceUID))); +} + + +void ImportWidget::onSeriesFoundResult(PACSSeriesInfo seriesInfo) +{ + m_seriesInfo.push_back(seriesInfo); + updateSeriesView(); +} + +void ImportWidget::onSeriesSelected(const QModelIndex ¤t) +{ + int row = current.row(); + if (row < 0) + return; + if (currentSeriesRow != row) + currentSeriesRow = row; + if (m_pProgressBar != nullptr) + m_pProgressBar->setValue(0); +} + + +void ImportWidget::onSeriesDoubleClicked(const QModelIndex ¤t) +{ + int row = current.row(); + if (row < 0) + return; + + if (currentSeriesRow != row && m_pProgressBar != nullptr) + { + m_pProgressBar->setValue(0); + currentSeriesRow = row; + } + + if (m_pMoveWorkerThread == nullptr) + { + m_pMoveWorkerThread = new QThread; + } + else + { + if (!m_pMoveWorkerThread->isFinished()) + { + m_pMoveWorkerThread->exit(); + m_pMoveWorkerThread->wait(); + } + } + + std::string seriesNumber = m_pSeriesModel->index(row, 1).data().toString().toStdString(); + std::string studyInstanceUID = m_pSeriesModel->index(row, 4).data().toString().toStdString(); + std::string studyID = m_pSeriesModel->index(row, 5).data().toString().toStdString(); + std::string seriesUID = m_pSeriesModel->index(row, 6).data().toString().toStdString(); + std::string patientName = m_pSeriesModel->index(row, 7).data().toString().toStdString(); + std::string outDirectory = DicomViewerHelper::applicationPath().toStdString(); + outDirectory += "/"; + outDirectory += patientName; + outDirectory += "/"; + outDirectory += studyID; + outDirectory += "/"; + outDirectory += seriesNumber; + + bool mkRet = false; + QDir d(QString::fromStdString(outDirectory)); + if (!d.exists()) + mkRet = d.mkpath(QString::fromStdString(outDirectory)); + + if (m_pMoveWorker == nullptr) + { + std::string peerIP, peerTitle, ourTitle; + unsigned long peerPort = 0, ourPort = 0; + getNetParams(peerIP, peerPort, peerTitle, ourPort, ourTitle); + if (peerIP.empty() || peerTitle.empty() || ourTitle.empty() || peerPort == 0 || ourPort == 0) + { + return; + } + + m_pMoveWorker = new MoveWorker(QString::fromStdString(peerIP), peerPort, QString::fromStdString(peerTitle), ourPort, QString::fromStdString(ourTitle), QString::fromStdString(outDirectory)); + QObject::connect(m_pMoveWorker, SIGNAL(notifyMoveDone(int, QString)), this, SLOT(moveDone(int, QString)), Qt::QueuedConnection); + QObject::connect(m_pMoveWorker, SIGNAL(notifyMoveProgress(int, int)), this, SLOT(moveProgress(int, int)), Qt::QueuedConnection); + QObject::connect(m_pMoveWorker, SIGNAL(notifyMoveStoreProgress(int, std::string)), this, SLOT(moveStoreProgress(int, std::string)), Qt::QueuedConnection); + } + m_pMoveWorker->setOutputDirectory(QString::fromStdString(outDirectory)); + m_pMoveWorker->moveToThread(m_pQueryWorkerThread); + m_pMoveWorkerThread->start(); + + //std::string studyInstanceUID = m_pStudyModel->index(row, 14).data().toString().toStdString(); + std::string seriesInstanceUID = m_pSeriesModel->index(row, 6).data().toString().toStdString(); + QMetaObject::invokeMethod(m_pMoveWorker, "moveBySeriesUID", Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(studyInstanceUID)), Q_ARG(QString, QString::fromStdString(seriesInstanceUID))); +} + + +void ImportWidget::moveDone(int code, QString dir) +{ + emit sigMoveDone(code, dir.toStdString()); +} + +void ImportWidget::moveProgress(int completed, int total) +{ + double fP = (double)completed / total; + int iP = (int)(fP * 100); + if (m_pProgressBar != nullptr) + m_pProgressBar->setValue(iP); +} + + +void ImportWidget::moveStoreProgress(int, std::string) +{ + +} + +void ImportWidget::updateSeriesView() +{ + m_pSeriesModel->clear(); + m_pSeriesModel->setHorizontalHeaderLabels(m_lSeriesHeaders); + int iRowIdx = 0; + + QList::const_iterator itr = m_seriesInfo.begin(); + for(; itr != m_seriesInfo.end(); ++itr) + { + int idx = iRowIdx + 1; + QString i; + i.setNum(idx); + QStandardItem* index = new QStandardItem(i); index->setEditable(false); + QStandardItem* number = new QStandardItem(itr->seriesNumber); number->setEditable(false); + QStandardItem* modality = new QStandardItem(itr->modality); modality->setEditable(false); + QStandardItem* Image = new QStandardItem(""); Image->setEditable(false); + QStandardItem* studyUID = new QStandardItem(itr->studyInstanceUID); studyUID->setEditable(false); + QStandardItem* studyID = new QStandardItem(itr->studyID); studyID->setEditable(false); + QStandardItem* seriesUID = new QStandardItem(itr->seriesUID); seriesUID->setEditable(false); + QStandardItem* patientName = new QStandardItem(itr->patientName); patientName->setEditable(false); + QList list; + list << index << number << modality << Image << studyUID << studyID << seriesUID << patientName; + m_pSeriesModel->insertRow(iRowIdx, list); + ++iRowIdx; + } + m_pSeriesResult->setModel(m_pSeriesModel); + + m_pSeriesResult->setColumnHidden(4, true); // StudyInstanceUID; + m_pSeriesResult->setColumnHidden(5, true); // StudyID; + m_pSeriesResult->setColumnHidden(6, true); // SeriesInstanceUID; + m_pSeriesResult->setColumnHidden(7, true); // PatientName; + + if (m_pProgressBar != nullptr) + m_pProgressBar->setValue(0); +} + +void ImportWidget::clear() +{ + m_pStudyModel->clear(); + m_pSeriesModel->clear(); +} + +void ImportWidget::configure() +{ + if (m_pSettingDialog == nullptr) + { + m_pSettingDialog = new ConfigurationDialog(); + connect(m_pSettingDialog, SIGNAL(updatePacsInfo()), this, SLOT(onPacsInfoUpdated())); + } + m_pSettingDialog->exec(); +} + +void ImportWidget::onPacsInfoUpdated() +{ + initFilterPacs(); +} + +void ImportWidget::onDateFilterChanged(const QString &text) +{ + if (text == "Custom date") + { + m_pStartDate->setEnabled(true); + m_pEndDate->setEnabled(false); + } + else if (text == "Custom date range") + { + m_pStartDate->setEnabled(true); + m_pEndDate->setEnabled(true); + } + else + { + m_pStartDate->setEnabled(false); + m_pEndDate->setEnabled(false); + } +} + +void ImportWidget::onTitleBarDestroyed() +{ + if (m_pTitleBar == QObject::sender()) + { + m_pTitleBar = Q_NULLPTR; + } +} \ No newline at end of file diff --git a/src/src/view/subview/metaDataWindow.cpp b/src/src/view/subview/metaDataWindow.cpp new file mode 100644 index 0000000..ed50670 --- /dev/null +++ b/src/src/view/subview/metaDataWindow.cpp @@ -0,0 +1,62 @@ +#include "metaDataWindow.h" +#include +#include +#include +#include + +#include "DicomTreeModel.h" +#include "include_dcmtk.h" + + + + + +metaDataWindow::metaDataWindow(DcmFileFormat *file,QWidget *parent): QMdiSubWindow(parent) +{ + m_file= file; + //m_dataset = file->getDataset(); + m_treeModel = new DicomTreeModel(m_file, this); + m_treeView = new QTreeView(); + m_treeView->setModel(m_treeModel); + //m_treeView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + //m_treeView->header()->setDefaultAlignment(Qt::AlignCenter); + m_treeView->expandAll(); + + m_treeView->setAlternatingRowColors(true); + m_treeView->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_treeView->setSelectionBehavior(QAbstractItemView::SelectItems); + m_treeView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + m_treeView->setAnimated(false); + m_treeView->setAllColumnsShowFocus(true); + + + for (int column = 0; column < m_treeModel->columnCount(); ++column) + m_treeView->resizeColumnToContents(column); + m_treeView->setColumnWidth(0, 150); + + m_valid = false; + + OFString PixelData; + if (m_file->getDataset()->findAndGetOFString(DCM_PixelData, PixelData).good()) + { + m_valid = true; + } + + QVBoxLayout *rootLayout = new QVBoxLayout(); + rootLayout->setMargin(0); + QWidget *rootWidget = new QWidget(); + + rootLayout->addWidget(m_treeView); + rootWidget->setLayout(rootLayout); + + layout()->addWidget(rootWidget); +} + +metaDataWindow::~metaDataWindow() +{ +} + +bool metaDataWindow::containsImage() const +{ + return m_valid; +} \ No newline at end of file diff --git a/src/src/view/subview/moveworker.cpp b/src/src/view/subview/moveworker.cpp new file mode 100644 index 0000000..aba6a0e --- /dev/null +++ b/src/src/view/subview/moveworker.cpp @@ -0,0 +1,60 @@ +#include "../../../include/view/subview/moveworker.h" + +MoveWorker::MoveWorker(const QString& peerIP, unsigned long long peerPort, const QString& peerTitle, unsigned long long ourPort, const QString& ourTitle, const QString& outputDirectory, QObject *parent) + : QObject(parent) + , m_pCallbackHelper(nullptr) +{ + m_strPeerIP_ = peerIP; + m_ulPeerPort_ = peerPort; + m_strPeerTitle_ = peerTitle; + m_ulOurPort_ = ourPort; + m_strOurTitle_ = ourTitle; + m_strOutputDirectory_ = outputDirectory; +} + +void MoveWorker::setOutputDirectory(const QString& outputDirectory) +{ + m_strOutputDirectory_ = outputDirectory; +} + +void MoveWorker::moveBySeriesUID(const QString& studyInstanceUID, const QString& seriesInstanceUID) +{ + std::string strPeerIP = m_strPeerIP_.toStdString(); + unsigned long long ullPeerPort = m_ulPeerPort_; + std::string strPeerTitle = m_strPeerTitle_.toStdString(); + unsigned long long ullOurPort = m_ulOurPort_; + std::string strOurTitle = m_strOurTitle_.toStdString(); + dcm_cmove *cmove = new dcm_cmove(strPeerIP.c_str(), ullPeerPort, strPeerTitle.c_str(), ullOurPort, strOurTitle.c_str()); + + CMoveCallback *moveCallback = new CMoveCallback(); + if (m_pCallbackHelper == nullptr) + { + m_pCallbackHelper = new CallbackHelper(); + connect(m_pCallbackHelper, SIGNAL(sig_moveProgress(int, int)), this, SLOT(onMoveProgress(int, int)), Qt::DirectConnection); + connect(m_pCallbackHelper, SIGNAL(sig_moveStoreProgress(int, std::string)), this, SLOT(onMoveStoreProgress(int, std::string)), Qt::DirectConnection); + } + moveCallback->setHelper(m_pCallbackHelper); + cmove->set_cmove_callback(moveCallback); + + std::string strOutputDirectory = m_strOutputDirectory_.toStdString(); + CMoveStoreSCPCallback *moveStoreCallback = new CMoveStoreSCPCallback(strOutputDirectory); + moveStoreCallback->setHelper(m_pCallbackHelper); + cmove->set_cmove_scp_callback(moveStoreCallback); + + std::string strStudyInstanceUID = studyInstanceUID.toStdString(); + std::string strSeriesInstanceUID = seriesInstanceUID.toStdString(); + int ret = cmove->move_by_series_uid(strStudyInstanceUID, strSeriesInstanceUID); + + emit notifyMoveDone(ret, m_strOutputDirectory_); + +} + +void MoveWorker::onMoveProgress(int progress, int total) +{ + emit notifyMoveProgress(progress, total); +} + +void MoveWorker::onMoveStoreProgress(int code, std::string filename) +{ + emit notifyMoveStoreProgress(code, filename); +} \ No newline at end of file diff --git a/src/src/view/subview/mytitlebar.cpp b/src/src/view/subview/mytitlebar.cpp new file mode 100644 index 0000000..b6ac0af --- /dev/null +++ b/src/src/view/subview/mytitlebar.cpp @@ -0,0 +1,281 @@ +#include "view/subview/mytitlebar.h" +#include +#include +#include +#include +#include "global/QGlobals.h" +#define BUTTON_HEIGHT 15 // Button height; +#define BUTTON_WIDTH 15 // Button width; +#define TITLE_HEIGHT 15 // Title bar height; + +static QString unpick_style = "*{background-color:#2d2d2d;border: 0px;}"; +static QString pick_style = "*{background-color:#463232;border: 0px;color:ebebc8;}"; + + +MyTitleBar::MyTitleBar(QWidget *parent) + : QFrame(parent) + //,m_color(UnpickTitleRGB) + , m_isPressed(false) +{ + // Initialization; + initControl(); + initConnections(); + // Load the local style MyTitle.css file; + //loadStyleSheet("MyTitle"); + SetHighlight(false); + this->setStyleSheet(unpick_style); +} + +MyTitleBar::~MyTitleBar() +{ + +} + +// Initialize the control; +void MyTitleBar::initControl() +{ + + m_pIcon = new QLabel(this); + m_pTitleContent = new QLabel(this); + + //m_pButtonMin = new QPushButton; + //m_pButtonRestore = new QPushButton; + m_pButtonMax = new QPushButton(this); + m_pButtonClose = new QPushButton(this); + + // m_pButtonMin->setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT)); + //m_pButtonRestore->setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT)); + m_pButtonMax->setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT)); + m_pButtonClose->setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT)); + + m_pTitleContent->setObjectName("TitleContent"); + // m_pButtonMin->setObjectName("ButtonMin"); + //m_pButtonRestore->setObjectName("ButtonRestore"); + m_pButtonMax->setObjectName("ButtonMax"); + m_pButtonClose->setObjectName("ButtonClose"); + + icon_max.addFile(QStringLiteral(MAX_URL), QSize(), QIcon::Normal, QIcon::Off); + icon_close.addFile(QStringLiteral(CLOSE_URL), QSize(), QIcon::Normal, QIcon::Off); + m_pButtonMax->setIcon(icon_max); + m_pButtonClose->setIcon(icon_close); + + + // m_pButtonMin->setToolTip(QStringLiteral("minimize")); + // m_pButtonRestore->setToolTip(QStringLiteral("reduction")); + m_pButtonMax->setToolTip(QStringLiteral("maximize")); + m_pButtonClose->setToolTip(QStringLiteral("shut down")); + + + QHBoxLayout* mylayout = new QHBoxLayout(this); + mylayout->addWidget(m_pIcon); + mylayout->addWidget(m_pTitleContent); + + //mylayout->addWidget(m_pButtonMin); + // mylayout->addWidget(m_pButtonRestore); + mylayout->addWidget(m_pButtonMax); + mylayout->addWidget(m_pButtonClose); + + mylayout->setContentsMargins(0, 0, 0, 0); + mylayout->setSpacing(0); + + m_pTitleContent->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + this->setFixedHeight(TITLE_HEIGHT); + this->setWindowFlags(Qt::FramelessWindowHint); + +} + +// Binding of signal slots; +void MyTitleBar::initConnections() +{ + //connect(m_pButtonMin, SIGNAL(clicked()), this, SLOT(onButtonMinClicked())); + //connect(m_pButtonRestore, SIGNAL(clicked()), this, SLOT(onButtonRestoreClicked())); + connect(m_pButtonMax, SIGNAL(clicked()), this, SLOT(onButtonMaxClicked())); + connect(m_pButtonClose, SIGNAL(clicked()), this, SLOT(onButtonCloseClicked())); +} + +// Set the background color of the title bar and draw the background color of the title bar in the paintEvent event; +// The default value is given in the constructor, you can set the color value externally to change the background color of the title bar; +void MyTitleBar::setBackgroundColor(QColor pickColor) +{ + this->setStyleSheet(pick_style); + //m_isTransparent = isTransparent; + // Repaint (call paintEvent event); + //m_color = pickColor; + // update(); +} + +// Set the title bar icon; +void MyTitleBar::setTitleIcon(QString filePath, QSize IconSize) +{ + QPixmap titleIcon(filePath); + m_pIcon->setPixmap(titleIcon.scaled(IconSize)); +} + +// Set the title content; +void MyTitleBar::setTitleContent(QString titleContent, int titleFontSize) +{ + // Set the title font size; + //QFont font = m_pTitleContent->font(); + //font.setPointSize(titleFontSize); + //m_pTitleContent->setFont(font); + // Set the title content; + m_pTitleContent->setText(titleContent); + m_titleContent = titleContent; +} + +// Set the length of the title bar; +void MyTitleBar::setTitleWidth(int width) +{ + this->setFixedWidth(width); +} + + +// Draw the background color of the title bar; +//void MyTitleBar::paintEvent(QPaintEvent *event) +//{ +// // Whether to set the title transparent; +// //if (!m_isTransparent) { +// //Set the background color; +// QPainter painter(this); +// QPainterPath pathBack; +// pathBack.setFillRule(Qt::WindingFill); +// pathBack.addRoundedRect(QRect(0, 0, this->width(), this->height()), 0, 0); +// painter.setRenderHint(QPainter::Antialiasing, true); +// painter.fillPath(pathBack, QBrush(m_color)); +// //} +// +// // When the window is maximized or restored, the length of the window changes, and the length of the title bar should be changed together; +// // The m_windowBorderWidth is subtracted here because the window may have a border of different width; +// // If the window has a border, you need to set the value of m_windowBorderWidth, otherwise m_windowBorderWidth defaults to 0; +// //if (this->width() != (this->parentWidget()->width() - m_windowBorderWidth)) +// //{ +// // this->setFixedWidth(this->parentWidget()->width() - m_windowBorderWidth); +// //} +// QFrame::paintEvent(event); +//} + +// Double-click to respond to events, mainly to achieve double-clicking the title bar to maximize and minimize operations; +void MyTitleBar::mouseDoubleClickEvent(QMouseEvent *event) +{ + //// Double-click is only valid when there are maximize and restore buttons; + //if (m_buttonType == MIN_MAX_BUTTON) + //{ + // // Determine whether the current window is maximized or original size by the state of the maximize button; + // // Or by setting variables separately to represent the current window state; + // if (m_pButtonMax->isVisible()) + // { + // onButtonMaxClicked(); + // } + // else + // { + // onButtonRestoreClicked(); + // } + //} + //emit signalButtonMaxClicked(); + return QWidget::mouseDoubleClickEvent(event); +} + +// The following three events, mousePressEvent, mouseMoveEvent, and mouseReleaseEvent, realize the effect of dragging the title bar with the mouse to move the window; +void MyTitleBar::mousePressEvent(QMouseEvent *event) +{ + //if (m_buttonType == MIN_MAX_BUTTON) + //{ + // // Do not drag the window when the window is maximized; + // if (m_pButtonMax->isVisible()) + // { + // m_isPressed = true; + // //m_startMovePos = event->globalPos(); + // } + //} + //else + //{ + // m_isPressed = true; + // //m_startMovePos = event->globalPos(); + //} + + + //emit signalViewClicked(); + return QWidget::mousePressEvent(event); +} + +void MyTitleBar::SetHighlight(bool yes) { + if (yes) { + this->setStyleSheet(pick_style); + //setStyleSheet(QString::fromUtf8("border:10px solid black")); + //setBackgroundColor(PickTitleRGB); + } + else { + this->setStyleSheet(unpick_style); + //setBackgroundColor(UnpickTitleRGB); + //setStyleSheet(QString::fromUtf8("border:10px solid red")); + } +} +//void MyTitleBar::mouseMoveEvent(QMouseEvent *event) +//{ +// if (m_isPressed) +// { +// QPoint movePoint = event->globalPos() - m_startMovePos; +// QPoint widgetPos = this->parentWidget()->pos(); +// m_startMovePos = event->globalPos(); +// this->parentWidget()->move(widgetPos.x() + movePoint.x(), widgetPos.y() + movePoint.y()); +// } +// return QWidget::mouseMoveEvent(event); +//} + +void MyTitleBar::mouseReleaseEvent(QMouseEvent *event) +{ + m_isPressed = false; + return QWidget::mouseReleaseEvent(event); +} + +// Load local style files; +// You can write the style directly in the file, and load it directly when the program is running; +void MyTitleBar::loadStyleSheet(const QString &sheetName) +{ + QFile file(":/Resources/" + sheetName + ".css"); + file.open(QFile::ReadOnly); + if (file.isOpen()) + { + QString styleSheet = this->styleSheet(); + styleSheet += QLatin1String(file.readAll()); + this->setStyleSheet(styleSheet); + } +} + +// The following is the slot for button operation response; +//void MyTitleBar::onButtonMinClicked() +//{ +// emit signalButtonMinClicked(); +//} +// +//void MyTitleBar::onButtonRestoreClicked() +//{ +// m_pButtonRestore->setVisible(false); +// m_pButtonMax->setVisible(true); +// emit signalButtonRestoreClicked(); +//} + +void MyTitleBar::onButtonMaxClicked() +{ + //m_pButtonMax->setVisible(false); + //m_pButtonRestore->setVisible(true); + emit signalButtonMaxClicked(); +} + +void MyTitleBar::onButtonCloseClicked() +{ + emit signalButtonCloseClicked(); +} + +// This method is mainly to display the title in the title bar as a scrolling effect; +//void MyTitleBar::onRollTitle() +//{ +// static int nPos = 0; +// QString titleContent = m_titleContent; +// // When the intercepted position is longer than the string, start from the beginning; +// if (nPos > titleContent.length()) +// nPos = 0; +// +// m_pTitleContent->setText(titleContent.mid(nPos)); +// nPos++; +//} diff --git a/src/src/view/subview/pacsconfiguration.cpp b/src/src/view/subview/pacsconfiguration.cpp new file mode 100644 index 0000000..2197e8e --- /dev/null +++ b/src/src/view/subview/pacsconfiguration.cpp @@ -0,0 +1,560 @@ +#include "view/subview/pacsconfiguration.h" +#include "view/subview/pacsconfiguretitlebar.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dialog/promptdialog.h" + +ConfigurationDialog::ConfigurationDialog(QWidget *parent) + : QDialog(parent) + , m_pOurInfoLayout(nullptr) + , m_pTitleBar(nullptr) + , m_pOurPortLabel(nullptr) + , m_pOurPortEdit(nullptr) + , m_pOurTitleLabel(nullptr) + , m_pOurTitleEdit(nullptr) + , m_AdvancedSettingsButton(nullptr) + , m_pOurInfoEndLine(nullptr) + , m_pToolsWidget(nullptr) + , m_pToolsLayout(nullptr) + , m_pTitleLabel(nullptr) + , m_pSpacerItem(nullptr) + , m_pDelButton(nullptr) + , m_pPacsInfo(nullptr) + , m_pEditWidget(nullptr) + , m_pEditLayout(nullptr) + , m_pPeerIpAddressLabel(nullptr) + , m_pPeerPortLabel(nullptr) + , m_pPeerTitleLabel(nullptr) + , m_pPeerDescritopnLabel(nullptr) + , m_pPeerIpAddressEdit(nullptr) + , m_pPeerPortEdit(nullptr) + , m_pPeerTitleEdit(nullptr) + , m_pPeerDescriptionEdit(nullptr) + , m_pAddButton(nullptr) + , m_pModifyButton(nullptr) + , m_pEditEndLine(nullptr) + , m_pActionWidget(nullptr) + , m_pActionLayout(nullptr) + , m_pSpacerItem1(nullptr) + , m_pSaveButton(nullptr) + , m_pCancelButton(nullptr) + , currentRow(-1) + , m_pMsgDialog(nullptr) +{ + initUi(); + initSys(); + initPacsInfo(); +} + + +ConfigurationDialog::~ConfigurationDialog() +{ +} + +void ConfigurationDialog::initUi() +{ + this->setWindowFlags(Qt::FramelessWindowHint); + this->resize(580, 380); + this->setMinimumSize(QSize(580, 380)); + + m_pMainLayout = new QVBoxLayout(this); + m_pTitleBar = new ConfigurationTitleBar(this); + m_pTitleBar->setFixedHeight(30); + m_pMainLayout->addWidget(m_pTitleBar); + + m_pOurInfoWidget = new QWidget(this); + m_pOurInfoLayout = new QHBoxLayout(this); + m_pOurPortLabel = new QLabel(m_pOurInfoWidget); + m_pOurPortLabel->setText(tr("Listener port:")); + m_pOurInfoLayout->addWidget(m_pOurPortLabel); + m_pOurPortEdit = new QLineEdit(m_pOurInfoWidget); + m_pOurInfoLayout->addWidget(m_pOurPortEdit); + m_pOurTitleLabel = new QLabel(m_pOurInfoWidget); + m_pOurTitleLabel->setText(tr("My AE title:")); + m_pOurInfoLayout->addWidget(m_pOurTitleLabel); + m_pOurTitleEdit = new QLineEdit(m_pOurInfoWidget); + m_pOurInfoLayout->addWidget(m_pOurTitleEdit); + m_AdvancedSettingsButton = new QPushButton(m_pOurInfoWidget); + m_AdvancedSettingsButton->setText(tr("Advanced settings")); + m_pOurInfoLayout->addWidget(m_AdvancedSettingsButton); + m_pOurInfoWidget->setLayout(m_pOurInfoLayout); + m_pMainLayout->addWidget(m_pOurInfoWidget); + + m_pOurInfoEndLine = new QFrame(this); + m_pOurInfoEndLine->setFrameShape(QFrame::HLine); + m_pOurInfoEndLine->setFrameShadow(QFrame::Sunken); + m_pMainLayout->addWidget(m_pOurInfoEndLine); + + m_pToolsWidget = new QWidget(this); + m_pToolsLayout = new QHBoxLayout(this); + m_pTitleLabel = new QLabel(m_pToolsWidget); + m_pTitleLabel->setText(tr("PACS location")); + m_pToolsLayout->addWidget(m_pTitleLabel); + m_pSpacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + m_pToolsLayout->addItem(m_pSpacerItem); + m_pDelButton = new QPushButton(m_pToolsWidget); + QIcon delIcon; + delIcon.addPixmap(QPixmap(QString::fromUtf8(":/importwidget/Resources/import/remove.png"))); + m_pDelButton->setIcon(delIcon); + m_pDelButton->setIconSize(QSize(22, 22)); + m_pDelButton->setStyleSheet("QPushButton{background:rgba(0,0,0,0);border:1px solid rgba(0,0,0,0);}"); + m_pToolsLayout->addWidget(m_pDelButton); + m_pToolsWidget->setLayout(m_pToolsLayout); + m_pMainLayout->addWidget(m_pToolsWidget); + + initHeaderInfo(); + m_pPacsInfo = new QTableView(this); + m_pPacsModel = new QStandardItemModel; + m_pPacsModel->setHorizontalHeaderLabels(m_lPacsInfoHeaders); + //m_pPacsSelectionModel = new QItemSelectionModel; + //m_pPacsInfo->setSelectionModel(m_pPacsSelectionModel); + m_pPacsInfo->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + m_pPacsInfo->verticalHeader()->setVisible(false); + m_pPacsInfo->setShowGrid(false); + m_pPacsInfo->setSelectionMode(QAbstractItemView::SingleSelection); + m_pPacsInfo->setSelectionBehavior(QAbstractItemView::SelectRows); + m_pPacsInfo->setModel(m_pPacsModel); + m_pMainLayout->addWidget(m_pPacsInfo); + + m_pEditWidget = new QWidget(this); + m_pEditLayout = new QGridLayout(this); + m_pPeerIpAddressLabel = new QLabel(m_pEditWidget); + m_pPeerIpAddressLabel->setText(tr("IP address")); + m_pPeerIpAddressLabel->setAlignment(Qt::AlignLeft); + m_pEditLayout->addWidget(m_pPeerIpAddressLabel, 0, 0, 1, 3); + m_pPeerPortLabel = new QLabel(m_pEditWidget); + m_pPeerPortLabel->setText(tr("Port")); + m_pPeerPortLabel->setAlignment(Qt::AlignLeft); + m_pEditLayout->addWidget(m_pPeerPortLabel, 0, 3, 1, 1); + m_pPeerTitleLabel = new QLabel(m_pEditWidget); + m_pPeerTitleLabel->setText(tr("AE title")); + m_pPeerTitleLabel->setAlignment(Qt::AlignLeft); + m_pEditLayout->addWidget(m_pPeerTitleLabel, 0, 4, 1, 2); + m_pPeerDescritopnLabel = new QLabel(m_pEditWidget); + m_pPeerDescritopnLabel->setText(tr("Description")); + m_pPeerDescritopnLabel->setAlignment(Qt::AlignLeft); + m_pEditLayout->addWidget(m_pPeerDescritopnLabel, 0, 6, 1, 5); + + m_pPeerIpAddressEdit = new QLineEdit(m_pEditWidget); + m_pEditLayout->addWidget(m_pPeerIpAddressEdit, 1, 0, 1, 3); + m_pPeerPortEdit = new QLineEdit(m_pEditWidget); + m_pEditLayout->addWidget(m_pPeerPortEdit, 1, 3, 1, 1); + m_pPeerTitleEdit = new QLineEdit(m_pEditWidget); + m_pEditLayout->addWidget(m_pPeerTitleEdit, 1, 4, 1, 2); + m_pPeerDescriptionEdit = new QLineEdit(m_pEditWidget); + m_pEditLayout->addWidget(m_pPeerDescriptionEdit, 1, 6, 1, 3); + m_pAddButton = new QPushButton(m_pEditWidget); + QIcon addIcon; + addIcon.addPixmap(QPixmap(QString::fromUtf8(":/importwidget/Resources/import/add.png"))); + m_pAddButton->setIcon(addIcon); + m_pAddButton->setIconSize(QSize(22, 22)); + m_pAddButton->setStyleSheet("QPushButton{background:rgba(0,0,0,0);border:1px solid rgba(0,0,0,0);}"); + m_pEditLayout->addWidget(m_pAddButton, 1, 9, 1, 1); + m_pModifyButton = new QPushButton(m_pEditWidget); + QIcon modIcon; + modIcon.addPixmap(QPixmap(QString::fromUtf8(":/importwidget/Resources/import/arrow.png"))); + m_pModifyButton->setIcon(modIcon); + m_pModifyButton->setIconSize(QSize(22, 22)); + m_pModifyButton->setStyleSheet("QPushButton{background:rgba(0,0,0,0);border:1px solid rgba(0,0,0,0);}"); + m_pEditLayout->addWidget(m_pModifyButton, 1, 10, 1, 1); + m_pEditWidget->setLayout(m_pEditLayout); + m_pMainLayout->addWidget(m_pEditWidget); + + m_pEditEndLine = new QFrame(this); + m_pEditEndLine->setFrameShape(QFrame::HLine); + m_pEditEndLine->setFrameShadow(QFrame::Sunken); + m_pMainLayout->addWidget(m_pEditEndLine); + + m_pActionWidget = new QWidget(this); + m_pActionLayout = new QHBoxLayout(m_pActionWidget); + m_pSpacerItem1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + m_pActionLayout->addItem(m_pSpacerItem1); + m_pSaveButton = new QPushButton(m_pActionWidget); + m_pSaveButton->setText(tr("Save")); + m_pActionLayout->addWidget(m_pSaveButton); + m_pCancelButton = new QPushButton(m_pActionWidget); + m_pCancelButton->setText(tr("Cancel")); + m_pActionLayout->addWidget(m_pCancelButton); + m_pMainLayout->addWidget(m_pActionWidget); + +} + +void ConfigurationDialog::initSys() +{ + m_pTitleBar->installEventFilter(this); + connect(m_pTitleBar, SIGNAL(sigClose()), this, SLOT(close())); + connect(m_pDelButton, SIGNAL(clicked()), this, SLOT(del())); + connect(m_pAddButton, SIGNAL(clicked()), this, SLOT(add())); + connect(m_pModifyButton, SIGNAL(clicked()), this, SLOT(modify())); + connect(m_pSaveButton, SIGNAL(clicked()), this, SLOT(save())); + connect(m_pCancelButton, SIGNAL(clicked()), this, SLOT(cancel())); + connect(m_pPacsInfo, SIGNAL(clicked(const QModelIndex &)), this, SLOT(onSelected(const QModelIndex &))); +} + +void ConfigurationDialog::initHeaderInfo() +{ + m_lPacsInfoHeaders.clear(); + m_lPacsInfoHeaders << tr("IP address") << tr("Port") << tr("AE title") + << tr("Description") << tr("Retrieve protocol") << tr("Maximum active downloads") + << tr("Preferred transfer syntax") << tr("Character set"); +} + +void ConfigurationDialog::initPacsInfo() +{ + m_pOurPortEdit->setText(DicomViewerHelper::ourPort()); + m_pOurTitleEdit->setText(DicomViewerHelper::ourTitle()); + + m_lHosts.clear(); + DicomViewerHelper::pacsInfo(m_lHosts); + m_lHostsNew.clear(); + DicomViewerHelper::pacsInfo(m_lHostsNew); + + m_pPacsModel->clear(); + m_pPacsModel->setHorizontalHeaderLabels(m_lPacsInfoHeaders); + int iRowIdx = 0; + for each (host var in m_lHostsNew) + { + ////QList list; + ////list << new QStandardItem(var.ip) \ + //// << new QStandardItem(var.port) \ + //// << new QStandardItem(var.ae) \ + //// << new QStandardItem(var.name) \ + //// << new QStandardItem("C-MOVE") \ + //// << new QStandardItem("1") \ + //// << new QStandardItem("Explicit VR LE") \ + //// << new QStandardItem("Default"); + ////m_pPacsModel->insertRow(iRowIdx, list); + ////++iRowIdx; + + QStandardItem* ip = new QStandardItem(var.ip); ip->setEditable(false); + QStandardItem* port = new QStandardItem(var.port); port->setEditable(false); + QStandardItem* ae = new QStandardItem(var.ae); ae->setEditable(false); + QStandardItem* name = new QStandardItem(var.name); name->setEditable(false); + QStandardItem* protocol = new QStandardItem("C-MOVE"); protocol->setEditable(false); + QStandardItem* count = new QStandardItem("1"); count->setEditable(false); + QStandardItem* xfers = new QStandardItem("Explicit VR LE"); xfers->setEditable(false); + QStandardItem* cset = new QStandardItem("Default"); cset->setEditable(false); + QList list; + list << ip << port << ae << name << protocol << count << xfers << cset; + m_pPacsModel->insertRow(iRowIdx, list); + ++iRowIdx; + } + + m_pPacsInfo->setModel(m_pPacsModel); +} + +void ConfigurationDialog::updatePacsView() +{ + m_pPacsModel->clear(); + m_pPacsModel->setHorizontalHeaderLabels(m_lPacsInfoHeaders); + int iRowIdx = 0; + + QList::ConstIterator itr = m_lHostsNew.begin(); + for (; itr != m_lHostsNew.end(); ++itr) + { + QStandardItem* ip = new QStandardItem(itr->ip); ip->setEditable(false); + QStandardItem* port = new QStandardItem(itr->port); port->setEditable(false); + QStandardItem* ae = new QStandardItem(itr->ae); ae->setEditable(false); + QStandardItem* name = new QStandardItem(itr->name); name->setEditable(false); + QStandardItem* protocol = new QStandardItem("C-MOVE"); protocol->setEditable(false); + QStandardItem* count = new QStandardItem("1"); count->setEditable(false); + QStandardItem* xfers = new QStandardItem("Explicit VR LE"); xfers->setEditable(false); + QStandardItem* cset = new QStandardItem("Default"); cset->setEditable(false); + QList list; + list << ip << port << ae << name << protocol << count << xfers << cset; + m_pPacsModel->insertRow(iRowIdx, list); + ++iRowIdx; + } + + //for each (host var in m_lHostsNew) + //{ + // QStandardItem* ip = new QStandardItem(var.ip); ip->setEditable(false); + // QStandardItem* port = new QStandardItem(var.port); port->setEditable(false); + // QStandardItem* ae = new QStandardItem(var.ae); ae->setEditable(false); + // QStandardItem* name = new QStandardItem(var.name); name->setEditable(false); + // QStandardItem* protocol = new QStandardItem("C-MOVE"); protocol->setEditable(false); + // QStandardItem* count = new QStandardItem("1"); count->setEditable(false); + // QStandardItem* xfers = new QStandardItem("Explicit VR LE"); xfers->setEditable(false); + // QStandardItem* cset = new QStandardItem("Default"); cset->setEditable(false); + // QList list; + // list << ip << port << ae << name << protocol << count << xfers << cset; + // m_pPacsModel->insertRow(iRowIdx, list); + // ++iRowIdx; + //} + m_pPacsInfo->setModel(m_pPacsModel); +} + + +void ConfigurationDialog::clearModify() +{ + m_pPeerIpAddressEdit->clear(); + m_pPeerPortEdit->clear(); + m_pPeerTitleEdit->clear(); + m_pPeerDescriptionEdit->clear(); +} + +void ConfigurationDialog::close() +{ + reject(); +} + + +void ConfigurationDialog::del() +{ + if (currentRow == -1) + return; + + QString name = m_pPacsModel->index(currentRow, 3).data().toString(); + int index = 0; + QList::iterator itr = m_lHostsNew.begin(); + for (; itr != m_lHostsNew.end(); ++itr) + { + if (name == itr->name) + m_lHostsNew.removeAt(index); + index++; + } + + //for each (host var in m_lHostsNew) + //{ + // if (name == var.name) + // m_lHostsNew.removeAt(index); + // index++; + //} + + updatePacsView(); + clearModify(); +} + +void ConfigurationDialog::save() +{ + DicomViewerHelper::setOurPort(m_pOurPortEdit->text()); + DicomViewerHelper::setOurTitle(m_pOurTitleEdit->text()); + DicomViewerHelper::setPacsInfo(m_lHostsNew); + emit updatePacsInfo(); + reject(); +} + +void ConfigurationDialog::cancel() +{ + reject(); +} + +// 直接判断name是否存在,不存在直接添加 +void ConfigurationDialog::add() +{ + QString name = m_pPeerDescriptionEdit->text(); + if (name.isEmpty()) + return; + + bool bFound = false; + QList::ConstIterator itr = m_lHostsNew.begin(); + for (; itr != m_lHostsNew.end(); ++itr) + { + if (itr->name == name) + { + bFound = true; + break; + } + } + + //for each (host var in m_lHostsNew) + //{ + // if (var.name == name) + // { + // bFound = true; + // break; + // } + //} + + if (bFound) + { + if (m_pMsgDialog == nullptr) + m_pMsgDialog = new PromptDialog("Alreay existed", this); + m_pMsgDialog->exec(); + return; + } + + host h; + h.name = name; + h.ae = m_pPeerTitleEdit->text(); + h.ip = m_pPeerIpAddressEdit->text(); + h.port = m_pPeerPortEdit->text(); + m_lHostsNew.push_back(h); + + updatePacsView(); + clearModify(); +} + +// 当前选中和修改的name是否一致,如果一致,直接修改当前选中 +// 如果不一致,判断是否存在,如果不存在,修改当前选中 +void ConfigurationDialog::modify() +{ + if (currentRow == -1) + return; + + QString selectedName = m_pPacsModel->index(currentRow, 3).data().toString(); + QString addName = m_pPeerDescriptionEdit->text(); + if (selectedName == addName) + { + QList::iterator itr = m_lHostsNew.begin(); + for (; itr != m_lHostsNew.end(); ++itr) + { + if (addName == itr->name) + { + itr->name = addName; + itr->ae = m_pPeerTitleEdit->text(); + itr->ip = m_pPeerIpAddressEdit->text(); + itr->port = m_pPeerPortEdit->text(); + updatePacsView(); + return; + } + } + + //for each (host var in m_lHostsNew) + //{ + // if (addName == var.name) + // { + // var.name = addName; + // var.ae = m_pPeerTitleEdit->text(); + // var.ip = m_pPeerIpAddressEdit->text(); + // var.port = m_pPeerPortEdit->text(); + // updatePacsView(); + // return; + // } + //} + } + else + { + bool bFound = false; + QList::iterator itr = m_lHostsNew.begin(); + for (; itr != m_lHostsNew.end(); ++itr) + { + if (addName == itr->name) + { + bFound = true; + break; + } + } + + //for each (host var in m_lHostsNew) + //{ + // if (addName == var.name) + // { + // bFound = true; + // break; + // } + //} + if (bFound) + { + if (m_pMsgDialog == nullptr) + m_pMsgDialog = new PromptDialog("Alreay existed", this); + m_pMsgDialog->exec(); + return; + } + else + { + itr = m_lHostsNew.begin(); + for (; itr != m_lHostsNew.end(); ++itr) + { + if (selectedName == itr->name) // 这里不要搞混了,是去更新选中的那一条 + { + itr->name = addName; + itr->ae = m_pPeerTitleEdit->text(); + itr->ip = m_pPeerIpAddressEdit->text(); + itr->port = m_pPeerPortEdit->text(); + updatePacsView(); + return; + } + } + //for each (host var in m_lHostsNew) + //{ + // if (selectedName == var.name) // 这里不要搞混了,是去更新选中的那一条 + // { + // var.name = addName; + // var.ae = m_pPeerTitleEdit->text(); + // var.ip = m_pPeerIpAddressEdit->text(); + // var.port = m_pPeerPortEdit->text(); + // updatePacsView(); + // return; + // } + //} + } + } +} + + +void ConfigurationDialog::onSelected(const QModelIndex ¤t) +{ + int row = current.row(); + currentRow = row; + if (row < 0) + return; + QString name = m_pPacsModel->index(row, 3).data().toString(); + QList::ConstIterator itr = m_lHostsNew.begin(); + for (; itr != m_lHostsNew.end(); ++itr) + { + if (name == itr->name) + { + m_pPeerIpAddressEdit->setText(itr->ip); + m_pPeerPortEdit->setText(itr->port); + m_pPeerTitleEdit->setText(itr->ae); + m_pPeerDescriptionEdit->setText(itr->name); + } + } + + //for each (host var in m_lHostsNew) + //{ + // if (name == var.name) + // { + // m_pPeerIpAddressEdit->setText(var.ip); + // m_pPeerPortEdit->setText(var.port); + // m_pPeerTitleEdit->setText(var.ae); + // m_pPeerDescriptionEdit->setText(var.name); + // } + //} +} + +void ConfigurationDialog::onTitleBarDestroyed() +{ + if (m_pTitleBar == QObject::sender()) + { + m_pTitleBar = Q_NULLPTR; + } +} + +bool ConfigurationDialog::eventFilter(QObject *obj, QEvent *event) +{ + if (m_pTitleBar == obj) + { + if (event->type() == QEvent::MouseButtonPress) + { + QRect dragArea = m_pTitleBar->geometry(); + if (dragArea.contains(mapFromGlobal(QCursor::pos())) && !this->isMaximized()) + { + m_dragState.dragging = true; + m_dragState.dragStartPosition = QCursor::pos(); + } + } + else if (event->type() == QEvent::MouseButtonRelease) + { + m_dragState.dragging = false; + } + else if (m_dragState.dragging && event->type() == QEvent::MouseMove) + { + const QPoint& curPos = QCursor::pos(); + this->move(this->geometry().topLeft() + (curPos - m_dragState.dragStartPosition)); + m_dragState.dragStartPosition = curPos; + } + } + return QDialog::eventFilter(obj, event); +} + diff --git a/src/src/view/subview/pacsconfiguretitlebar.cpp b/src/src/view/subview/pacsconfiguretitlebar.cpp new file mode 100644 index 0000000..8492863 --- /dev/null +++ b/src/src/view/subview/pacsconfiguretitlebar.cpp @@ -0,0 +1,67 @@ +#include "view/subview/pacsconfiguretitlebar.h" +#include +#include +#include +#include + +ConfigurationTitleBar::ConfigurationTitleBar(QWidget *parent) + : QWidget(parent) + , m_pMainLayout(nullptr) + , m_pLogoLabel(nullptr) + , m_pTitleLabel(nullptr) + , m_pSpacerItem(nullptr) + , m_pCloseButton(nullptr) +{ + initUi(); + initSys(); +} + +ConfigurationTitleBar::~ConfigurationTitleBar() +{ + +} + +void ConfigurationTitleBar::setTitleText(const QString& title) +{ + m_pTitleLabel->setText(title); +} + +void ConfigurationTitleBar::initUi() +{ + m_pMainLayout = new QHBoxLayout(this); + m_pMainLayout->setContentsMargins(0, 0, 0, 0); + + m_pLogoLabel = new QLabel(this); + QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(m_pLogoLabel->sizePolicy().hasHeightForWidth()); + m_pLogoLabel->setSizePolicy(sizePolicy); + m_pLogoLabel->setBaseSize(30, 30); + m_pLogoLabel->setPixmap(QPixmap(QString::fromUtf8(":/importwidget/Resources/import/icon.png"))); + m_pLogoLabel->setScaledContents(true); + m_pMainLayout->addWidget(m_pLogoLabel); + + m_pTitleLabel = new QLabel(this); + m_pTitleLabel->setText(tr("PACS configuration")); + m_pMainLayout->addWidget(m_pTitleLabel); + + m_pSpacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + m_pMainLayout->addItem(m_pSpacerItem); + + m_pCloseButton = new QPushButton(this); + QIcon closeIcon; + closeIcon.addPixmap(QPixmap(QString::fromUtf8(":/importwidget/Resources/import/close.png"))); + m_pCloseButton->setIcon(closeIcon); + m_pCloseButton->setIconSize(QSize(22, 22)); + m_pCloseButton->setStyleSheet("QPushButton{background:rgba(0,0,0,0);border:1px solid rgba(0,0,0,0);}"); + m_pMainLayout->addWidget(m_pCloseButton); +} + +void ConfigurationTitleBar::initSys() +{ + connect(m_pCloseButton, SIGNAL(clicked()), this, SIGNAL(sigClose())); +} + + + diff --git a/src/src/view/subview/queryworker.cpp b/src/src/view/subview/queryworker.cpp new file mode 100644 index 0000000..acf6b37 --- /dev/null +++ b/src/src/view/subview/queryworker.cpp @@ -0,0 +1,254 @@ +#include "../../../include/view/subview/queryworker.h" +#include "dcmtk/dcmdata/dcdatset.h" + +QueryWorker::QueryWorker(const QString& peerIP, unsigned long peerPort, const QString& peerTitle, const QString& ourTitle, QObject *parent) + : QObject(parent) + , m_pCallbackHelper(nullptr) +{ + m_strPeerIP_ = peerIP; + m_ulPeerPort_ = peerPort; + m_strPeerTitle_ = peerTitle; + m_strOurTitle_ = ourTitle; + m_iQueryLevel = 0; +} + +void QueryWorker::setPacsInfo(const QString& peerIP, unsigned long peerPort, const QString& peerTitle, const QString& ourTitle) +{ + m_strPeerIP_ = peerIP; + m_ulPeerPort_ = peerPort; + m_strPeerTitle_ = peerTitle; + m_strOurTitle_ = ourTitle; +} + +void QueryWorker::queryByPatientName(const QString& patientName) +{ + m_iQueryLevel = 0; + std::string strPeerIP = m_strPeerIP_.toStdString(); + unsigned long ulPort = m_ulPeerPort_; + std::string strPeerTitle = m_strPeerTitle_.toStdString(); + std::string strOurTitle = m_strOurTitle_.toStdString(); + dcm_cfind *cfind = new dcm_cfind(strPeerIP.c_str(), ulPort, strPeerTitle.c_str(), strOurTitle.c_str()); + + CFindCallback *findCallback = new CFindCallback(); + if (m_pCallbackHelper == nullptr) + { + m_pCallbackHelper = new CallbackHelper(); + connect(m_pCallbackHelper, SIGNAL(sig_foundResult(int, DcmDataset *)), this, SLOT(onFoundResult(int, DcmDataset *)), Qt::DirectConnection); + } + findCallback->setHelper(m_pCallbackHelper); + cfind->set_find_callback(findCallback); + + int ret = cfind->find_by_patient_name(patientName.toStdString()); + + emit sendFindDone(ret); + + delete findCallback; + delete cfind; +} + +void QueryWorker::queryByPatientNameAndDate(const QString& patientName, const QString& studyStartDate, const QString& studyEndDate) +{ + QString patientName_ = "*"; + patientName_ += patientName; + patientName_ += "*"; + + m_iQueryLevel = 0; + std::string strPeerIP = m_strPeerIP_.toStdString(); + unsigned long ulPort = m_ulPeerPort_; + std::string strPeerTitle = m_strPeerTitle_.toStdString(); + std::string strOurTitle = m_strOurTitle_.toStdString(); + dcm_cfind *cfind = new dcm_cfind(strPeerIP.c_str(), ulPort, strPeerTitle.c_str(), strOurTitle.c_str()); + + CFindCallback *findCallback = new CFindCallback(); + if (m_pCallbackHelper == nullptr) + { + m_pCallbackHelper = new CallbackHelper(); + connect(m_pCallbackHelper, SIGNAL(sig_foundResult(int, DcmDataset *)), this, SLOT(onFoundResult(int, DcmDataset *)), Qt::DirectConnection); + } + findCallback->setHelper(m_pCallbackHelper); + cfind->set_find_callback(findCallback); + + int ret = cfind->find_by_patient_name_and_date(patientName_.toStdString(), studyStartDate.toStdString(), studyEndDate.toStdString()); + + emit sendFindDone(ret); + + delete findCallback; + delete cfind; +} + +void QueryWorker::queryByPatientID(const QString& patientID) +{ + m_iQueryLevel = 0; + std::string strPeerIP = m_strPeerIP_.toStdString(); + unsigned long ulPort = m_ulPeerPort_; + std::string strPeerTitle = m_strPeerTitle_.toStdString(); + std::string strOurTitle = m_strOurTitle_.toStdString(); + dcm_cfind *cfind = new dcm_cfind(strPeerIP.c_str(), ulPort, strPeerTitle.c_str(), strOurTitle.c_str()); + + CFindCallback *findCallback = new CFindCallback(); + if (m_pCallbackHelper == nullptr) + { + m_pCallbackHelper = new CallbackHelper(); + connect(m_pCallbackHelper, SIGNAL(sig_foundResult(int, DcmDataset *)), this, SLOT(onFoundResult(int, DcmDataset *)), Qt::DirectConnection); + } + findCallback->setHelper(m_pCallbackHelper); + cfind->set_find_callback(findCallback); + + int ret = cfind->find_by_patient_id(patientID.toStdString()); + + emit sendFindDone(ret); + + delete findCallback; + delete cfind; +} + +void QueryWorker::queryByPatientIDAndDate(const QString& patientID, const QString& studyStartDate, const QString& studyEndDate) +{ + m_iQueryLevel = 0; + std::string strPeerIP = m_strPeerIP_.toStdString(); + unsigned long ulPort = m_ulPeerPort_; + std::string strPeerTitle = m_strPeerTitle_.toStdString(); + std::string strOurTitle = m_strOurTitle_.toStdString(); + dcm_cfind *cfind = new dcm_cfind(strPeerIP.c_str(), ulPort, strPeerTitle.c_str(), strOurTitle.c_str()); + + CFindCallback *findCallback = new CFindCallback(); + if (m_pCallbackHelper == nullptr) + { + m_pCallbackHelper = new CallbackHelper(); + connect(m_pCallbackHelper, SIGNAL(sig_foundResult(int, DcmDataset *)), this, SLOT(onFoundResult(int, DcmDataset *)), Qt::DirectConnection); + } + findCallback->setHelper(m_pCallbackHelper); + cfind->set_find_callback(findCallback); + + int ret = cfind->find_by_patient_id_and_date(patientID.toStdString(), studyStartDate.toStdString(), studyEndDate.toStdString()); + + emit sendFindDone(ret); + + delete findCallback; + delete cfind; +} + +void QueryWorker::queryByAccessNo(const QString& accsessionNo) +{ + m_iQueryLevel = 0; + std::string strPeerIP = m_strPeerIP_.toStdString(); + unsigned long ulPort = m_ulPeerPort_; + std::string strPeerTitle = m_strPeerTitle_.toStdString(); + std::string strOurTitle = m_strOurTitle_.toStdString(); + dcm_cfind *cfind = new dcm_cfind(strPeerIP.c_str(), ulPort, strPeerTitle.c_str(), strOurTitle.c_str()); + + CFindCallback *findCallback = new CFindCallback(); + if (m_pCallbackHelper == nullptr) + { + m_pCallbackHelper = new CallbackHelper(); + connect(m_pCallbackHelper, SIGNAL(sig_foundResult(int, DcmDataset *)), this, SLOT(onFoundResult(int, DcmDataset *)), Qt::DirectConnection); + } + findCallback->setHelper(m_pCallbackHelper); + cfind->set_find_callback(findCallback); + + int ret = cfind->find_by_accession_no(accsessionNo.toStdString()); + + emit sendFindDone(ret); + + delete findCallback; + delete cfind; +} + +void QueryWorker::queryByAccessNoAndDate(const QString& accessionNo, const QString& studyStartDate, const QString& studyEndDate) +{ + m_iQueryLevel = 0; + std::string strPeerIP = m_strPeerIP_.toStdString(); + unsigned long ulPort = m_ulPeerPort_; + std::string strPeerTitle = m_strPeerTitle_.toStdString(); + std::string strOurTitle = m_strOurTitle_.toStdString(); + dcm_cfind *cfind = new dcm_cfind(strPeerIP.c_str(), ulPort, strPeerTitle.c_str(), strOurTitle.c_str()); + + CFindCallback *findCallback = new CFindCallback(); + if (m_pCallbackHelper == nullptr) + { + m_pCallbackHelper = new CallbackHelper(); + connect(m_pCallbackHelper, SIGNAL(sig_foundResult(int, DcmDataset *)), this, SLOT(onFoundResult(int, DcmDataset *)), Qt::DirectConnection); + } + findCallback->setHelper(m_pCallbackHelper); + cfind->set_find_callback(findCallback); + + int ret = cfind->find_by_accession_no_and_date(accessionNo.toStdString(), studyStartDate.toStdString(), studyEndDate.toStdString()); + + emit sendFindDone(ret); + + delete findCallback; + delete cfind; +} + +void QueryWorker::queryBySeriesUID(const QString& studyInstanceUID) +{ + m_iQueryLevel = 1; + std::string strPeerIP = m_strPeerIP_.toStdString(); + unsigned long ulPort = m_ulPeerPort_; + std::string strPeerTitle = m_strPeerTitle_.toStdString(); + std::string strOurTitle = m_strOurTitle_.toStdString(); + dcm_cfind *cfind = new dcm_cfind(strPeerIP.c_str(), ulPort, strPeerTitle.c_str(), strOurTitle.c_str()); + + CFindCallback *findCallback = new CFindCallback(); + if (m_pCallbackHelper == nullptr) + { + m_pCallbackHelper = new CallbackHelper(); + connect(m_pCallbackHelper, SIGNAL(sig_foundResult(int, DcmDataset *)), this, SLOT(onFoundResult(int, DcmDataset *)), Qt::DirectConnection); + } + findCallback->setHelper(m_pCallbackHelper); + cfind->set_find_callback(findCallback); + + int ret = cfind->find_by_series_uid(studyInstanceUID.toStdString()); + emit sendFindDone(ret); + + delete findCallback; + delete cfind; +} + +void QueryWorker::onFoundResult(int index, DcmDataset *response) +{ + std::string studyDate_, patientName_, patientID_, accessionNumber_, + patientBirthDate_, patientSex_, patientAge_, studyInstanceUID_, + studyID_, requestingPhysician_, seriesUid_, modality_, seriesNumber_; + response->findAndGetOFString(DCM_StudyDate, studyDate_); + response->findAndGetOFString(DCM_PatientName, patientName_); + response->findAndGetOFString(DCM_PatientID, patientID_); + response->findAndGetOFString(DCM_AccessionNumber, accessionNumber_); + response->findAndGetOFString(DCM_PatientBirthDate, patientBirthDate_); + response->findAndGetOFString(DCM_PatientSex, patientSex_); + response->findAndGetOFString(DCM_PatientAge, patientAge_); + response->findAndGetOFString(DCM_StudyInstanceUID, studyInstanceUID_); + response->findAndGetOFString(DCM_StudyID, studyID_); + response->findAndGetOFString(DCM_RequestingPhysician, requestingPhysician_); + response->findAndGetOFString(DCM_SeriesInstanceUID, seriesUid_); + response->findAndGetOFString(DCM_Modality, modality_); + response->findAndGetOFString(DCM_SeriesNumber, seriesNumber_); + + if (m_iQueryLevel == 0) + { + PACSStudyInfo study; + study.studyDate = QString::fromStdString(studyDate_); + study.patientName = QString::fromStdString(patientName_); + study.patientID = QString::fromStdString(patientID_); + study.accessionNumber = QString::fromStdString(accessionNumber_); + study.patientBirthDate = QString::fromStdString(patientBirthDate_); + study.patientSex = QString::fromStdString(patientSex_); + study.patientAge = QString::fromStdString(patientAge_); + study.studyInstanceUID = QString::fromStdString(studyInstanceUID_); + study.studyID = QString::fromStdString(studyID_); + study.requestingPhysician = QString::fromStdString(requestingPhysician_); + emit sendStudyItemFound(study); + } + else + { + PACSSeriesInfo series; + series.patientName = QString::fromStdString(patientName_); + series.studyInstanceUID = QString::fromStdString(studyInstanceUID_); + series.studyID = QString::fromStdString(studyID_); + series.seriesUID = QString::fromStdString(seriesUid_); + series.modality = QString::fromStdString(modality_); + series.seriesNumber = QString::fromStdString(seriesNumber_); + emit sendSeriesItemFound(series); + } + +} \ No newline at end of file diff --git a/src/src/view/subview/radiusprogressbar.cpp b/src/src/view/subview/radiusprogressbar.cpp new file mode 100644 index 0000000..5311342 --- /dev/null +++ b/src/src/view/subview/radiusprogressbar.cpp @@ -0,0 +1,40 @@ +#include "../../../include/view/subview/radiusprogressbar.h" +#include + +RadiusProgressBar::RadiusProgressBar(QWidget *parent) + : QProgressBar(parent) +{ + setMinimum(0); + setMaximum(0); + setValue(0); +} + +RadiusProgressBar::~RadiusProgressBar() +{ + +} + +void RadiusProgressBar::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + QRect rect = QRect(0, 0, width(), height() / 2); + QRect textRect = QRect(0, height() / 2, width(), height() / 2); + + const double k = (double)(value() - minimum()) / (maximum() - minimum()); + int x = (int)(rect.width() * k); + QRect fillRect = rect.adjusted(0, 0, x - rect.width(), 0); + + QString valueStr = QString("%1%").arg(QString::number(value())); + QPixmap buttomMap = QPixmap(":/importwidget/Resources/import/radius_back.png"); + QPixmap fillMap = QPixmap(":/importwidget/Resources/import/radius_front.png"); + + p.drawPixmap(rect, buttomMap); + p.drawPixmap(fillRect, fillMap, fillRect); + + + QFont f = QFont("Microsoft YaHei", 15, QFont::Bold); + p.setFont(f); + p.setPen(QColor("#555555")); + p.drawText(textRect, Qt::AlignCenter, valueStr); +} + diff --git a/src/src/view/thumbnailImage.cpp b/src/src/view/thumbnailImage.cpp new file mode 100644 index 0000000..5be1769 --- /dev/null +++ b/src/src/view/thumbnailImage.cpp @@ -0,0 +1,166 @@ +#include "view/thumbnailImage.h" +#include +#include +#include +#include "global/QGlobals.h" +#include "qstyleoption.h" +#include "qpainter.h" + +static QString unpick_style = "*{background-color:#7f7f7f;border: 1px;}" +"QLabel#m_descri{color:white}"; + +static QString pick_style = "*{background-color:#c5c5c5;border: 1px;}" +"QLabel#m_descri{color:black;}"; + +thumbnailImage::thumbnailImage(QWidget *parent, SeriesInfo_t* series_info) : + m_series_info(series_info) + //, m_color(UnpickThumbRGB) + , QFrame(parent) +{ + + this->setObjectName(QString::fromUtf8("frame")); + //this->setGeometry(QRect(170, 90, 141, 141)); + this->setFrameShape(QFrame::WinPanel); + this->setFrameShadow(QFrame::Raised); + this->setLineWidth(2); + QVBoxLayout *verticalLayout = new QVBoxLayout(this); + verticalLayout->setSpacing(0); + verticalLayout->setObjectName(QString::fromUtf8("verticalLayout")); + verticalLayout->setContentsMargins(0, 0, 0, 0); + m_descri = new QLabel(this); + m_descri->setObjectName(QString::fromUtf8("m_descri")); + QFont font; + font.setBold(true); + font.setWeight(75); + m_descri->setFont(font); + m_descri->setFrameShape(QFrame::NoFrame); + m_descri->setFrameShadow(QFrame::Plain); + m_descri->setAlignment(Qt::AlignCenter); + + verticalLayout->addWidget(m_descri); + + + QWidget* widget_2 = new QWidget(this); + widget_2->setObjectName(QString::fromUtf8("widget_2")); + QHBoxLayout* horizontalLayout_2 = new QHBoxLayout(widget_2); + horizontalLayout_2->setObjectName(QString::fromUtf8("horizontalLayout_2")); + QSpacerItem* horizontalSpacer_2 = new QSpacerItem(5, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + horizontalLayout_2->setContentsMargins(0, 0, 0, 0); + horizontalLayout_2->setSpacing(0); + horizontalLayout_2->addItem(horizontalSpacer_2); + + m_pixmap = new QLabel(widget_2); + m_pixmap->setObjectName(QString::fromUtf8("m_pixmap")); + QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(m_pixmap->sizePolicy().hasHeightForWidth()); + m_pixmap->setSizePolicy(sizePolicy); + m_pixmap->setMinimumSize(QSize(100, 100)); + m_pixmap->setMaximumSize(QSize(100, 100)); + m_pixmap->setLayoutDirection(Qt::LeftToRight); + m_pixmap->setFrameShape(QFrame::StyledPanel); + m_pixmap->setPixmap(series_info->series_pixmap); + m_pixmap->setScaledContents(true); + m_pixmap->setAlignment(Qt::AlignCenter); + horizontalLayout_2->addWidget(m_pixmap); + QSpacerItem* horizontalSpacer_3 = new QSpacerItem(6, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + horizontalLayout_2->addItem(horizontalSpacer_3); + verticalLayout->addWidget(widget_2); + + + + QWidget* widget = new QWidget(this); + widget->setObjectName(QString::fromUtf8("widget")); + QHBoxLayout* horizontalLayout = new QHBoxLayout(widget); + horizontalLayout->setSpacing(0); + horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout")); + horizontalLayout->setContentsMargins(0, 0, 0, 0); + QSpacerItem *horizontalSpacer = new QSpacerItem(114, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + horizontalLayout->addItem(horizontalSpacer); + + m_slicenum = new QLabel(widget); + m_slicenum->setMinimumSize(QSize(20, 15)); + m_slicenum->setMaximumSize(QSize(20, 15)); + m_slicenum->setObjectName(QString::fromUtf8("m_slicenum")); + m_slicenum->setStyleSheet(QString::fromUtf8("background-color:black;color:white;")); + m_slicenum->setTextFormat(Qt::PlainText); + m_slicenum->setAlignment(Qt::AlignCenter); + //m_slicenum->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); + + horizontalLayout->addWidget(m_slicenum); + verticalLayout->addWidget(widget); + + m_descri->setText(QString::fromStdString(series_info->tag_info->m_SeriesDescription)); + m_slicenum->setText(QString::fromStdString(series_info->tag_info->m_SliceNumber)); +} + +//void thumbnailImage::setBackgroundColor(QColor pickColor) +//{ +// //m_color = pickColor; +// //update(); +//} +//void thumbnailImage::paintEvent(QPaintEvent *event) +//{ +// // Whether to set the title transparent; +// //if (!m_isTransparent) { +// //Set the background color; +// QPainter painter(this); +// QPainterPath pathBack; +// pathBack.setFillRule(Qt::WindingFill); +// pathBack.addRoundedRect(QRect(0, 0, this->width(), this->height()), 0, 0); +// painter.setRenderHint(QPainter::Antialiasing, true); +// painter.fillPath(pathBack, QBrush(m_color)); +// //} +// QWidget::paintEvent(event); +//} + +void thumbnailImage::setHighlight(bool yes) { + if (yes) { + //setBackgroundColor(PickThumbRGB); + this->setStyleSheet(pick_style); + } + else { + //setBackgroundColor(UnpickThumbRGB); + this->setStyleSheet(unpick_style); + } +} + +//void thumbnailImage::setHighlight(bool yes) { +// QPalette p = palette(); +// if (yes) { +// setAutoFillBackground(true); +// p.setColor(QPalette::Background, Qt::gray); +// } +// else { +// //p.setColor(QPalette::Background, Qt::darkGray); +// setAutoFillBackground(false); +// } +// setPalette(p); +//} +void thumbnailImage::mouseReleaseEvent(QMouseEvent* event) +{ + emit Signal_ThumbClicked(this); +} +void thumbnailImage::mousePressEvent(QMouseEvent* event) +{ + drag_org_ = event->pos(); +} + +void thumbnailImage::mouseMoveEvent(QMouseEvent *e) { + if ((e->buttons() & Qt::LeftButton) && + ((e->pos() - drag_org_).manhattanLength() > + QApplication::startDragDistance())) { + QDrag *drag = new QDrag(this); + QMimeData *data = new QMimeData; + data->setText(QString::number((qulonglong)this)); + drag->setMimeData(data); + drag->exec(Qt::CopyAction); + } + //QLabel::mouseMoveEvent(e); +} + +thumbnailImage::~thumbnailImage() +{ + +} diff --git a/src/src/view/thumbnailbarwidget.cpp b/src/src/view/thumbnailbarwidget.cpp new file mode 100644 index 0000000..d30df10 --- /dev/null +++ b/src/src/view/thumbnailbarwidget.cpp @@ -0,0 +1,301 @@ +锘#include "view/thumbnailbarwidget.h" +#include "view/thumbnailImage.h" +#include "base/DicomLoader.h" +#include "base/seriesinstance.h" +#include +#include +#include +#include + +#include "view/dicomimageview.h" +#include "qstyleoption.h" +#include "qpainter.h" +/************************************************************************ +* Function List: +* 1. [Done][Action Open] update thumbnail bar whenever open a directory or file +* 2. [Abort][Click] create a seriesIntance showing on chosen DicomImageView +* 3. [Done][Highlight] when the relevant view is clicked,set thumbnail highlight +* 4. [Done][Drag Motion] the same as click +* 5. [Undone][Scroll] scroll bar to show all thumbnails +* +* Consider: +* the difference between action open and click. +* 1. [Done]improve the current map to support multiple instances +* 2. [Done]think about how different instances share the same image data + you may use smart pointer to address this problem, init a instance with another +* 3. [Done]updateThumbnailBar will remove all widget fist which costs high, need to be improved +* +* Notice : +* 1. Drag logic is confused with click, take place of click +* +* Replace Strategy +* 1. file->dir[same series uid] replace all the opening file +* 2. file->file[different instance number] replace all the opening file[Radiant:combine images together] +* 3. dir->dir[same series uid] do nothing +* 4. file->file[same instance number] do nothing +* Namely, existing instances are all dir or same single file +* +/************************************************************************/ + + + +class ThumbMessages +{ +public: + static std::string Format(PatientInfo_t *patient) + { + std::stringstream tmp; + //tmp << "PATINET" << "\n"; + tmp << patient->patient_name << "\n" << patient->birth_date; + return tmp.str(); + } + static std::string Format(StudyInfo_t *study) + { + std::stringstream tmp; + //tmp << "STUDY" << "\n"; + tmp << study->study_date << "\n" << study->study_description; + return tmp.str(); + } + + static std::string Format(SeriesInfo_t *series) + { + std::stringstream tmp; + tmp << series->open_mode << "\n"; + //tmp << series->instance_num << "\n" << series->open_mode; + return tmp.str(); + } + +}; + + +ThumbnailBarWidget::ThumbnailBarWidget(QWidget *parent) : + QFrame(parent), + currentImageLabel(nullptr) { + setFocusPolicy(Qt::StrongFocus); + + scrollArea = new QScrollArea(this); + QBoxLayout *layout0 = new QVBoxLayout(this); + this->setLayout(layout0); + layout0->addWidget(scrollArea); + layout0->setContentsMargins(0, 0, 0, 0); + scrollArea->setFrameShape(QFrame::NoFrame); + + //QBoxLayout *layout1 = new QVBoxLayout(scrollArea); + //this->setLayout(layout1); + //layout1->addWidget(seriesPanel); + //layout1->setContentsMargins(0, 0, 0, 0); + seriesPanel = new QWidget(scrollArea); + scrollArea->setWidget(seriesPanel); + scrollArea->setWidgetResizable(true); + + + + seriesPanel->setObjectName("seriesPanel"); + QBoxLayout *layout = new QVBoxLayout(seriesPanel); + seriesPanel->setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(1); + layout->setAlignment(Qt::AlignTop); + + this->setObjectName("thumbnailbar"); + QString style = + "*{background-color:#646464;color:white;font:10px;}" + "QPushButton#patient{background-color: #5f4141;font:13px;}" + "QPushButton#study{background-color: #6d5555;}"; + this->setStyleSheet(style); +} + +//------------------------------------------------------- +ThumbnailBarWidget::~ThumbnailBarWidget() { + clear(); + //emit Signal_QuitFileWatcher(); +} + +//------------------------------------------------------- +void ThumbnailBarWidget::updateThumbnailBar() +{ + foreach(QWidget *l, LabelList) { + seriesPanel->layout()->removeWidget(l); + LabelList.removeOne(l); + //delete l; + } + //foreach(thumbnailImage *t, imageLabelList) { + // imageLabelList.removeOne(t); + // //delete l; + //} + + + + + DicomLoader *helper = DicomLoader::GetInstance(); + const PatientsMapType & all_patients = helper->getPatientsList(); + for (PatientsMapType::const_iterator it_pa = all_patients.cbegin(); it_pa != all_patients.cend(); it_pa++) + { + QPushButton *patient_lbl = it_pa->second->patient_label; + if (!patient_lbl) + { + patient_lbl = new QPushButton(seriesPanel); + patient_lbl->setObjectName("patient"); + std::string text_pa = ThumbMessages::Format(it_pa->second); + QString qtext = QString::fromStdString(text_pa); + qtext.replace("^", "\n"); + patient_lbl->setText(qtext); + patient_lbl->setEnabled(false); + //patient_lbl->setGeometry(0, 0, 100, 100); + //QFont font; + //font.setFamily(QString::fromUtf8("Britannic Bold")); + //patient_lbl->setFont(font); + //patient_lbl->setWordWrap(true); + //patient_lbl->setFrameShape(QFrame::NoFrame); + } + seriesPanel->layout()->addWidget(patient_lbl); + LabelList << patient_lbl; + + + StudiesMapType *studies = it_pa->second->studies; + for (StudiesMapType::const_iterator it_st = studies->cbegin(); it_st != studies->cend(); it_st++) + { + QPushButton *study_lbl = it_st->second->study_label; + if (!study_lbl) + { + study_lbl = new QPushButton(seriesPanel); + study_lbl->setObjectName("study"); + std::string text_st = ThumbMessages::Format(it_st->second); + //study_lbl->setGeometry(0, 0, 100, 100); + study_lbl->setText(QString::fromStdString(text_st)); + study_lbl->setEnabled(false); + //QFont font; + //font.setFamily(QString::fromUtf8("Britannic Bold")); + //study_lbl->setFont(font); + //study_lbl->setWordWrap(true); + //study_lbl->setFrameShape(QFrame::NoFrame); + } + seriesPanel->layout()->addWidget(study_lbl); + //layout->addWidget(study_lbl); + LabelList << study_lbl; + + + SeriesMapType *series = it_st->second->series; + for (SeriesMapType::const_iterator it_se = series->cbegin(); it_se != series->cend(); it_se++) + { + thumbnailImage *thumbnail = it_se->second->thumb_nail; + if (it_se->second->pixmap_valid==false) + { + //OVERRIDE_LEVEL + if (thumbnail) + { + //if thumbnail is currentImageLabel,set nullptr, or crash + if (currentImageLabel == thumbnail) + { + currentImageLabel = nullptr; + } + delete thumbnail; + thumbnail = nullptr; + } + //wait for update + //thumbnail = new thumbnailImage(this, it_se->second); + thumbnail = createThumbnailImage(seriesPanel, it_se->second); + + //update pointer point to new thumbnail + it_se->second->thumb_nail = thumbnail; + it_se->second->pixmap_valid = true; + } + seriesPanel->layout()->addWidget(thumbnail); + //save all thumbnail in one list + LabelList << thumbnail; + //imageLabelList << thumbnail; + + } + + + } + + } +} +thumbnailImage* ThumbnailBarWidget::createThumbnailImage(QWidget *parent, SeriesInfo_t* series_info) +{ + + thumbnailImage* t = new thumbnailImage(parent, series_info); + //connect(t, &thumbnailImage::Signal_ThumbClicked,this, &ThumbnailBarWidget::SLot_ThumbClicked); + connect(t, SIGNAL(Signal_ThumbClicked(thumbnailImage*)), this, SLOT(SLot_ThumbClicked(thumbnailImage*))); + return t; + +} + +//void ThumbnailBarWidget::paintEvent(QPaintEvent *) +//{ +// QStyleOption o; +// o.initFrom(this); +// QPainter p(this); +// style()->drawPrimitive( +// QStyle::PE_Widget, &o, &p, this); +//} + +void ThumbnailBarWidget::Slot_setCurrentThumbnail(DicomImageView *view) +{ + + //cancel all highlight; + //if (currentImageLabel) + //{ + // currentImageLabel->setHighlight(false); + //} + + if (view != nullptr) + { + if (view->HasSeries()) { + SeriesInstance *series = view->getSeriesInstance(); + //set to another + DicomLoader *helper = DicomLoader::GetInstance(); + SeriesInfo_t* cur_serie = helper->getSerieInfo(*series->getUniqueID()); + if (cur_serie) + { + thumbnailImage *thumb = cur_serie->thumb_nail; + if (thumb) + { + setCurrentImageLabel(thumb); + } + } + } + else + { + if (currentImageLabel) { + currentImageLabel->setHighlight(false); + currentImageLabel = nullptr; + } + } + } + //else + //{ + // currentImageLabel->setHighlight(false); + // currentImageLabel = nullptr; + //} +} + +void ThumbnailBarWidget::SLot_ThumbClicked(thumbnailImage* thumb) +{ + setCurrentImageLabel(thumb); + //SeriesInfo_t* serie_info = thumb->getSeriesInfo(); + emit Signal_ThumbClicked(thumb); + +} + + +//------------------------------------------------------- +void ThumbnailBarWidget::setCurrentImageLabel(thumbnailImage *imageLabel) { + if (currentImageLabel != imageLabel) { + if (currentImageLabel) { + currentImageLabel->setHighlight(false); + } + currentImageLabel = imageLabel; + if (currentImageLabel) { + currentImageLabel->setHighlight(true); + } + } +} + +//------------------------------------------------------- +void ThumbnailBarWidget::clear() { + //qDeleteAll(imageLabelList); + //imageLabelList.clear(); + currentImageLabel = nullptr; +} diff --git a/src/src/view/viewcontainerwidget.cpp b/src/src/view/viewcontainerwidget.cpp new file mode 100644 index 0000000..96ab8e7 --- /dev/null +++ b/src/src/view/viewcontainerwidget.cpp @@ -0,0 +1,503 @@ +锘#include "viewcontainerwidget.h" +#include "thumbnailImage.h" +#include "DicomLoader.h" +#include +#include +#include +#include +#include "QGlobals.h" +#include +#include "qstyleoption.h" +#include "qpainter.h" +#include "pqVCRToolbar.h" +#include +//------------------------------------------------------- + + +ViewContainerWidget::ViewContainerWidget(QWidget *parent) :QFrame(parent) +{ + QGridLayout * layout = new QGridLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + this->setLayout(layout); + Slot_SetViewLayout(1, 1); +} + +//------------------------------------------------------- +ViewContainerWidget::~ViewContainerWidget() { + +} + +//------------------------------------------------------- +QList ViewContainerWidget::getViewList() const { + return view_list_; +} + + +DicomImageView *ViewContainerWidget::getCurrentView() const { + return current_view_; +} + +DicomImageView* ViewContainerWidget::getNextView() const +{ + bool found = false; + for (int i = 0; i < view_list_.size(); ++i) + { + DicomImageView *v = view_list_.at(i); + if (found) + { + return v; + } + + if (current_view_ == v) + { + found = true; + } + } + return nullptr; +} + + + +SeriesInstance* ViewContainerWidget::getCurrentSeries() const +{ + if (current_view_ != nullptr) + { + if (current_view_->HasSeries()) + { + return current_view_->getSeriesInstance(); + } + } + return nullptr; + +} + + +void ViewContainerWidget::getRelevantViewList(const UniqueIDInfo& unique, QList & viewList) +{ + for (auto *v : view_list_) { + if (v->HasSeries()) + { + UniqueIDInfo_t* exist = v->getSeriesInstance()->getUniqueID(); + if (DicomUtil::EqualsUnique(*exist, unique)) + { + viewList.push_back(v); + }; + + } + } +} + +void ViewContainerWidget::Slot_SyncEvent(DicomImageView *view, int interactionMode, void* calldata) +{ + + switch (interactionMode) + { + case VTKIS_IMAGE_SLICING: + if (SyncHelper::getSyncItem(SLICE_POS)) + { + if (SyncHelper::getSyncState() == AUTO_SYNC) + { + for (auto *v : view_list_) { + //foreach(DicomImageView *v, view_list_){ + + if (v->HasSeries()) + { + if (view != v) + { + int* r = (int*)calldata; + v->setScrollChangedType(scrollScope::TriggerType::SYNC_ONLY); + v->SetSlice(r[0]); +#ifdef _DEBUG + printf("Sliced, current slice number:%d \r\n", r[0]); +#endif // _DEBUG + //v->GetScrollbar()->setValue(r[0]); + v->setScrollChangedType(scrollScope::TriggerType::USER_TRIGGER); + } + } + } + } + if (SyncHelper::getSyncState() == MANUAL_SYNC) + { + + //foreach(DicomImageView *v, view_list_) { + for (auto *v : view_list_) { + if (v->HasSeries()) + { + if (v != view) + { + //disable global trigger slot + v->setScrollChangedType(scrollScope::TriggerType::SYNC_ONLY); + int* r = (int*)calldata; + //r[0]:cur_value + //r[1]:view_step + //r[2]:min_value + //r[3]:max_value + //int set_value = r[0] + r[1]; + //v->GetScrollbar()->setValue(set_value); + v->AddSlice(r[1]); +#ifdef _DEBUG + printf("Sliced, current added slice number:%d \r\n", r[1]); +#endif + v->setScrollChangedType(scrollScope::TriggerType::USER_TRIGGER); + + } + } + + } + } + + } + break; + case VTKIS_IMAGE_PAN: + if (SyncHelper::getSyncItem(ZOOM_PAN)) + { + //foreach(DicomImageView *v, view_list_) { + for (auto *v : view_list_) { + if (v != view) + { + double *d = (double *)calldata; + double vector[3] = { d[3] - d[0],d[4] - d[1],d[5] - d[2] }; + v->SetPanOffset(vector); +#ifdef _DEBUG + printf("EndPan, last focalpoint:%f,%f,%f;current focalpoint:%f,%f,%f \r\n", d[0], d[1], d[2], d[3], + d[4], d[5]); +#endif + } + } + } + break; + case VTKIS_IMAGE_ZOOM: + if (SyncHelper::getSyncItem(ZOOM_PAN)) + { + //foreach(DicomImageView *v, view_list_) { + for (auto *v : view_list_) { + if (v != view) + { + double *d = (double *)calldata; + + v->SetZoomScale(d[1]); +#ifdef _DEBUG + printf("EndDolly, scale param:%f,%f \r\n", d[0], d[1]); +#endif + } + } + } + break; + case VTKIS_IMAGE_WINDOWLEVEL: + if (SyncHelper::getSyncItem(WIDTH_LEVEL)) + { + for (auto *v : view_list_) { + //foreach(DicomImageView *v, view_list_) { + if (v != view) + { + double *d = (double *)calldata; + printf("EndWindowLevel, scale param:%f,%f \r\n", d[0], d[1]); + v->setWindowLevel(d[1], d[0]); + } + } + } + break; + default: + break; + } +} + + +void ViewContainerWidget::SetInteractionMode(int InteractionMode) +{ + for (auto *v : view_list_) { + myQVTKOpenGLNativeWidget*gl_w = v->getGLWidget(); + if (gl_w->GetRenderWindow() && gl_w->GetRenderWindow()->GetInteractor() && v->HasSeries()) + { + infinitiViewer* viewer = v->getSeriesInstance()->getImageViewer2(); + viewer->GetInteractorStyle()->SetInteractionModeFromEnum(InteractionMode); + } + } +} + +void ViewContainerWidget::Slot_ViewClicked(DicomImageView *view) { + //set current_view + if (current_view_ != view) { + setCurrentView(view); + } + +} + +void ViewContainerWidget::setCurrentView(DicomImageView *view) { + //notify before set nullptr + if (current_view_) { + current_view_->setHighlight(false); + } + current_view_ = view; + if (current_view_) { + current_view_->setHighlight(true); + } + emit Signal_NotifyThumbnail(current_view_); +} + +//------------------------------------------------------- +/** + * @brief ViewContainerWidget::Slot_ViewDoubleClicked + * 鍙屽嚮 view(鏀惧ぇ缂╁皬) + * @param view + */ + + +void ViewContainerWidget::Slot_ViewDoubleClicked(DicomImageView *view) { + if (!view) { + return; + } + if (maxed_) { + //DicomImageView::setFontRatio(_fontRatio); + //std::for_each(view_list_.begin(), view_list_.end(), [=](DicomImageView* v) { + for (auto *v : view_list_) { + v->setVisible(true); + } + maxed_ = false; + + } + else { + //std::for_each(view_list_.begin(), view_list_.end(), [=](DicomImageView* v){ + for (auto *v : view_list_) { + v->setVisible(false); + } + view->setVisible(true); + maxed_ = true; + + } +} + +//------------------------------------------------------- +/** + * @brief ViewContainerWidget::Slot_SetViewLayout + * @param col + * @param row + */ + + + +void ViewContainerWidget::Slot_SetViewLayout(int col, int row) { + + if (!(col > 0 && row > 0)) { + return; + } + + for (auto *v : view_list_) { + this->layout()->removeWidget(v); + } + int viewCount = col * row; + while (viewCount < view_list_.size()) { + DicomImageView *v = view_list_.takeLast(); + //YTC:better compare before delete + if (current_view_ == v) + { + setCurrentView(view_list_.first()); + } + //current_view_ = (current_view_ == v) ? nullptr : current_view_; + delete v; + } + // + DicomImageView *view; + int tl_x = this->geometry().topLeft().x(); + int tl_y = this->geometry().topLeft().y(); + int br_x = this->geometry().bottomRight().x(); + int br_y = this->geometry().bottomRight().y(); + int grid_x = (br_x - tl_x) / col; + int grid_y = (br_y - tl_y) / row; + + + for (int i = 0; i < row; ++i) { + for (int j = 0; j < col; ++j) { + if (i * col + j < view_list_.size()) { + view = view_list_[i * col + j]; + } + else { + view = createImageView(this); + view_list_ << view; + + } + if (view->IsCine()) + { + //delay + pqVCRToolbar* vcr_tool = view->getVCRToolbar(); + int n_x = tl_x + (j + 0.5) * grid_x - VCRHelper::vcr_toolbar_offset; + int n_y = tl_y + (i + 1)*grid_y; + vcr_tool->move(n_x, n_y); + } + + view->getGLWidget()->show(); + QGridLayout *layout = qobject_cast(this->layout()); + layout->addWidget(view, i, j); + + } + } + if ((!current_view_) && (!view_list_.isEmpty())) { + Slot_ViewClicked(view_list_.first()); + } +} + +//------------------------------------------------------- +DicomImageView *ViewContainerWidget::createImageView(QWidget* parent) { + DicomImageView *v = new DicomImageView(parent); + + connect(v, &DicomImageView::Signal_ViewEmpty, + this, &ViewContainerWidget::Slot_ViewEmpty); + + connect(v, &DicomImageView::Signal_ViewClicked, + this, &ViewContainerWidget::Slot_ViewClicked); + + connect(v, &DicomImageView::Signal_viewDoubleclicked, + this, &ViewContainerWidget::Slot_ViewDoubleClicked); + + + connect(v, &DicomImageView::Signal_DragDropEvent, + this, &ViewContainerWidget::Slot_DragDropEvent); + + connect(v, &DicomImageView::Signal_SyncEvent, + this, &ViewContainerWidget::Slot_SyncEvent); + + return v; +} + +void ViewContainerWidget::resizeEvent(QResizeEvent *e) { + QWidget::resizeEvent(e); +} + +void ViewContainerWidget::emptyCurrentView() +{ + Slot_ViewEmpty(current_view_); +} + + + +void ViewContainerWidget::Slot_ViewEmpty(DicomImageView *view) +{ + + if (view != nullptr) + { + view->ResetView(); + } + + ////default notify is true + //if (current_view_ == view) + //{ + // setCurrentView(view_list_.first()); + //} + +} + +void ViewContainerWidget::Slot_DragDropEvent(DicomImageView *view, thumbnailImage* tb) +{ + SeriesInfo_t* serie_info = tb->getSeriesInfo(); + SeriesInstance* old = nullptr; + bool copy = true; + + if (view->HasSeries()) + { + old = view->getSeriesInstance(); + } + replaceViewWithSerie(serie_info->unique_info, serie_info->tag_info, view, old, copy); + setCurrentView(view); +} + +void ViewContainerWidget::Slot_ThumbnailClickEvent(thumbnailImage* tb) +{ + DicomImageView *view = getCurrentView(); + if (nullptr == view) + { + return; + } + SeriesInfo_t* serie_info = tb->getSeriesInfo(); + SeriesInstance* old = nullptr; + bool copy = true; + + if (view->HasSeries()) + { + old = view->getSeriesInstance(); + } + replaceViewWithSerie(serie_info->unique_info, serie_info->tag_info, view, old, copy); + setCurrentView(view); +} + +void ViewContainerWidget::replaceViewWithSerie(UniqueIDInfo_t* unique_info, DicomTagInfo_t* tag_info, DicomImageView* curV, SeriesInstance* old, bool copy) +{ + curV->removeViewWithFusion(); + + DicomLoader *helper = DicomLoader::GetInstance(); + vtkGenericOpenGLRenderWindow* grw = curV->getRenWin(); + SeriesInstance* instance = helper->createSeries(unique_info, tag_info, grw, copy); + helper->addSeriesInstance(instance, copy); + curV->setDicomImageView(instance); + curV->Render(); + //delete old after new instance render + if (old) + { + helper->deleteSeriesInstance(old); + } +} + +void ViewContainerWidget::toggleViewWithFusion() +{ + if (current_view_) + { + + if (current_view_->HasSeries() && current_view_->IsFusion()) + { + removeCurrentViewWithFusion(); + } + else + { + replaceViewWithFusion(); + } + } + +} + +void ViewContainerWidget::replaceViewWithFusion() +{ + //Temporal: automatically fusion with next image + DicomImageView * overlap_view = this->getNextView(); + if (checkFusionStatus(current_view_, overlap_view)) + { + connect(overlap_view, &DicomImageView::Signal_WindowLevelEventForFusion, + current_view_, &DicomImageView::Slot_WindowLevelEventForFusion); + + current_view_->SetFusionInput(overlap_view); + current_view_->Render(); + } + else + { + QMessageBox::information(this, "Warning", "Invalid Fusion!"); + } +} +void ViewContainerWidget::removeCurrentViewWithFusion() +{ + if (current_view_) { + current_view_->removeViewWithFusion(); + } +} + + + +bool ViewContainerWidget::checkFusionStatus(DicomImageView *base, DicomImageView* overlap) +{ + if (nullptr == base || nullptr == overlap) + { + + return false; + } + if (!base->HasSeries() || !overlap->HasSeries()) + { + return false; + } + if (base->getSeriesNumber() != DicomModality::ReflMode) + { + return false; + } + if (overlap->getSeriesNumber() == DicomModality::AttMode || overlap->getSeriesNumber() == DicomModality::SosMode) + { + return true; + } + return false; +} \ No newline at end of file