第六章 基于QT和DCMTK的Dicom 图像浏览器---Dicom视图类

由于mitk.net被人恶意抢注, 中科院分子影像重点实验室的www.mitk.net 改到 www.mitk.net.cn 。

目录:

开始  《DCMTK(MD版)编译和安装+VS2015》

第一章 《DCMTK(MD版)、QT、VS2015编写Dicom序列浏览应用程序-新建项目,配置环境》

第二章 《第二章 基于QT和DCMTK的Dicom 图像浏览器---界面设计》

第三章 《 基于QT和DCMTK的Dicom 图像浏览器---单个Dicom图像读取类》

第四章 《基于QT和DCMTK的Dicom 图像浏览器---检查文件夹下Dicom序列个数》

第五章 《基于QT和DCMTK的Dicom 图像浏览器---Dicom图像序列类

第六章  《基于QT和DCMTK的Dicom 图像浏览器---Dicom视图类

第七章  《基于QT和DCMTK的Dicom 图像浏览器---收尾》

一、设计视图类DicomView

1) 在项目上右键->添加->Add Qt Class ,  按照向导添加类 DicomView,如下图

2) DicomView类继承 QGraphicsView,构造标签为QWidget *parent,如下图

3) 编辑DicomView.h  和 cpp

#pragma once
#include <QGraphicsView>

#include "SeriesBase.h"

class QGraphicsScene;
class QGraphicsPixmapItem;
class QGraphicsWidget;
class DicomView : public QGraphicsView
{
	Q_OBJECT

public:
	enum viewStatus {
		Browse,
		Zoom,//缩放
		Pan, // 移动
		Magnifier, // 放大镜
		Drawing,// 画
	};
	
	DicomView(QWidget *parent=0);
	~DicomView();

	void setSeries(SeriesBase*series);
	void resizePixmapItem(); // 窗口改变时
	void repositionAuxItems(); // 窗口改变时
	void updateView();
	void setHighlight(bool yes); // 边框

	public slots:
	void setCurFrameItem(int); 

signals:
	void windowChanged();// 窗宽位改变信号
	void clicked(SeriesBase::ViewType);// 点击

protected:
	void mousePressEvent(QMouseEvent *event); // 鼠标按住时触发
	void mouseMoveEvent(QMouseEvent *event); // 鼠标移动时触发
	void mouseReleaseEvent(QMouseEvent *event); // 鼠标释放后触发
	void wheelEvent(QWheelEvent *e); // 滚轮出发
	void resizeEvent(QResizeEvent *event); // 窗口调整时运行
	QSize sizeHint() const { return hintSize; }

private:
	QGraphicsScene *scene;// 场景
	QGraphicsPixmapItem *pixmapItem;//图像项 场景中的图像
	QGraphicsSimpleTextItem *posValueItem;// 文本项 显示当前鼠标值
	QGraphicsSimpleTextItem *curFrameItem; //  文本项 显示当前帧
	QGraphicsWidget *slider;// 控制条

	SeriesBase *dicoms; // 该视图显示的数据
	QSize hintSize;
	double factor;
	double fixFactor;// xspace/yspace 宽高的比例
	QPoint prevMousePos; // 
	viewStatus status;
};
#include "DicomView.h"

#include <QGraphicsPixmapItem>
#include <QGraphicsWidget>
#include <QGraphicsProxyWidget>
#include <QGraphicsLinearLayout>
#include <QMouseEvent>
#include <qslider.h>

DicomView::DicomView(QWidget *parent)
	: QGraphicsView(parent)
{
	scene = new QGraphicsScene(this);
	pixmapItem = new QGraphicsPixmapItem;
	posValueItem = new QGraphicsSimpleTextItem;
	curFrameItem = new QGraphicsSimpleTextItem;
	dicoms = 0;
	status = Browse;

	setFocusPolicy(Qt::StrongFocus);
	setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
	setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
	setContextMenuPolicy(Qt::DefaultContextMenu);
	setBackgroundBrush(QBrush(Qt::black));
	setAcceptDrops(true);
	setFrameShape(QFrame::Box);
	setFrameShadow(QFrame::Plain);
	setAlignment(Qt::AlignCenter);
	setResizeAnchor(QGraphicsView::AnchorViewCenter);
	setHighlight(false);

	scene->setSceneRect(-5000, -5000, 10000, 10000);
	setScene(scene);

	// 图片切换风格
	pixmapItem->setTransformationMode(Qt::SmoothTransformation);
	pixmapItem->setAcceptHoverEvents(true);
	posValueItem->setBrush(Qt::magenta);
	curFrameItem->setBrush(Qt::magenta);

	scene->addItem(pixmapItem);
	scene->addItem(posValueItem);
	scene->addItem(curFrameItem);

	slider = new QGraphicsWidget;
	
	QGraphicsLinearLayout *verticalLayout = new QGraphicsLinearLayout;
	verticalLayout->setOrientation(Qt::Vertical);
	verticalLayout->setSpacing(0);

	slider->setLayout(verticalLayout);

	scene->addItem(slider);
}

DicomView::~DicomView()
{
}

void DicomView::setSeries(SeriesBase * series)
{
	if (!series)
		return;
	if (!series->isNormal())
		return;
	if (dicoms == 0)
	{
		// 把dicoms->slider 添加到 slider
		QGraphicsProxyWidget*pw = scene->addWidget(series->slider);
		QGraphicsLinearLayout *verticalLayout = (QGraphicsLinearLayout *)slider->layout();
		verticalLayout->addItem(pw);
	}

	this->dicoms = series;
	dicoms->setDefaultWindow();
	emit windowChanged();
	dicoms->gotoFrame(dicoms->getFrameCount() / 2);
	connect(dicoms->slider, SIGNAL(valueChanged(int)), this, SLOT(setCurFrameItem(int)));

	double xSpacing = 0, ySpacing = 0;
	if (dicoms->getPixSpacing(xSpacing, ySpacing)) {
		if (xSpacing > 0.000001 && ySpacing > 0.000001) {
			double psX = xSpacing;
			double psY = ySpacing;
			fixFactor = psY / psX;
		}
	}

	pixmapItem->setPos(0, 0);
	pixmapItem->setRotation(0);
	pixmapItem->resetTransform();

	updateView();
	
	scene->update(scene->sceneRect());
}

void DicomView::resizePixmapItem()
{
	if (!pixmapItem->pixmap().isNull()) {
		QRectF pixmapRect = pixmapItem->boundingRect();
		QRectF viewRect = this->rect();

		//if (!manualZoom) {
			if (pixmapRect.width()*viewRect.height() < pixmapRect.height()*fixFactor*viewRect.width())
				factor = viewRect.height() / (pixmapRect.height()*fixFactor);
			else
				factor = viewRect.width() / pixmapRect.width();
		//}

		pixmapItem->setTransform(QTransform(factor, 0, 0, factor*fixFactor, 0, 0));
		// if (!manualPan)
			centerOn(pixmapItem);
	}
}

void DicomView::repositionAuxItems()
{
	QPointF sceneTL = mapToScene(rect().topLeft());
	QPointF sceneBR = mapToScene(rect().bottomRight());

	curFrameItem->setPos(sceneBR.x() - 68, sceneTL.y()+18);

	slider->setPos(sceneBR.x() - 58, sceneTL.y() + 58);

	QSizeF slider_size(58, sceneBR.y() - sceneTL.y() - 116);
	slider->resize(slider_size);
	posValueItem->setPos(sceneTL.x()+ 10, sceneBR.y()- 58);
}

void DicomView::updateView()
{
	QPixmap pixmap;
	if (dicoms&&dicoms->isNormal()) {
		dicoms->getPixmap(pixmap);
		pixmapItem->setPixmap(pixmap);
		pixmapItem->setTransformOriginPoint(pixmapItem->boundingRect().center());
	}
	else {
		pixmapItem->setPixmap(pixmap);
		curFrameItem->setText("");
		posValueItem->setText("");
	}

	resizePixmapItem();
	repositionAuxItems();
}

void DicomView::setHighlight(bool yes)
{
}

void DicomView::setCurFrameItem(int)
{
	if (!dicoms || !dicoms->isNormal())
		return;
	curFrameItem->setText(tr("%1-%2").arg(dicoms->getCurFrame()).arg(dicoms->getFrameCount()));
}

void DicomView::mousePressEvent(QMouseEvent * event)
{
	if (dicoms&&dicoms->isNormal())
	{
		emit clicked(dicoms->getViewType());
		QPointF sp = mapToScene(event->pos());
		QPointF ip = pixmapItem->mapFromScene(sp);
		prevMousePos = event->pos();

		if (event->button() == Qt::LeftButton) {
			setDragMode(QGraphicsView::NoDrag);
			switch (status) {
			case Browse:
				if (!(event->modifiers()&Qt::ControlModifier)) {
					setDragMode(QGraphicsView::RubberBandDrag);
					setCursor(Qt::ArrowCursor); // 划框
				}
				break;
			}
		}
		QGraphicsView::mousePressEvent(event);
	}
}

void DicomView::mouseMoveEvent(QMouseEvent * event)
{
	if (dicoms&&dicoms->isNormal()) {
		QPointF sp = mapToScene(event->pos());
		QPointF ip = pixmapItem->mapFromScene(sp);
		if (pixmapItem->contains(ip)) {
			QPoint pos = ip.toPoint();
			QString s = dicoms->getPixelValue(pos.x(), pos.y());
			posValueItem->setText(tr("(%1, %2):").arg(pos.x()).arg(pos.y())+ s);
		}
	}

	// 按住右键调整窗宽窗位
	if (event->buttons()&Qt::RightButton) {
		// 调整窗宽位
		setDragMode(QGraphicsView::NoDrag);
		setCursor(Qt::ArrowCursor);
		QPoint delta = event->pos() - prevMousePos;
		dicoms->setWindowDelta(-delta.y() * 16, delta.x() * 16);
		emit windowChanged();
		updateView();
	}
	QGraphicsView::mouseMoveEvent(event);
}

void DicomView::mouseReleaseEvent(QMouseEvent * event)
{
	if (dicoms&&dicoms->isNormal()) {
		if (event->button() == Qt::LeftButton) {
			switch (status) {
			case Browse:
				// 划框时结束时调整窗宽窗位
				if (scene->selectedItems().size() == 0)
				{
					if (rubberBandRect().isValid())
					{
						if (dicoms->getViewType() == SeriesBase::XY)
						{
							dicoms->setRoiWindow(pixmapItem->mapFromScene(mapToScene(rubberBandRect())).boundingRect());
							emit windowChanged();
							updateView();
						}

					}
				}
				break;
			}
		}
	}

	QGraphicsView::mouseReleaseEvent(event);
}

void DicomView::wheelEvent(QWheelEvent * e)
{
	QPoint delta = e->angleDelta();
	if (dicoms == 0 || !dicoms->isNormal()) return;

	if (delta.y() > 0) dicoms->prevFrame();
	else if (delta.y() < 0) dicoms->nextFrame();
	updateView();
}

void DicomView::resizeEvent(QResizeEvent * event)
{
	resizePixmapItem();
	repositionAuxItems();
}

 

二、提升UI文件QGraphicsView 为 DicomView

 

保存提升后的ui,运行调试,就会发现显示框变黑了,提升成功。 

 

三、实验“选择序列”下拉菜单功能

    ...
class SeriesBase; // 添加
class DicomBrowse : public QMainWindow
{
        ...
	public slots:
        ...
	void on_windowChanged();
private:
        ...
	SeriesBase *xyS;// 添加  xy视图的序列
	SeriesBase *xzS;// 添加
	SeriesBase *yzS;// 添加
}
...
#include "SeriesBase.h"  // 添加
#include "XYSeries.h"
#include "XZSeries.h"	
#include "YZSeries.h"

...
DicomBrowse::DicomBrowse(QWidget *parent)
	: QMainWindow(parent)
{
	...
	connect(ui.graphicsView_XY, SIGNAL(windowChanged()), this, SLOT(on_windowChanged()));
	xyS = new XYSeries(); // 添加
	xzS = new XZSeries(); 
	yzS = new YZSeries();
}

DicomBrowse::~DicomBrowse()
{
	delete xyS;
	delete xzS;
	delete yzS;
}
...
// 点击“选择序列”下拉菜单后执行
void  DicomBrowse::on_comboBox_which_currentIndexChanged(QString seriesUID)
{
	if (isOpeing)
		return;
	isOpeing = true;
	SeriesBase::readSeriesUid(seriesUID); // 读取序列
	isOpeing = false;
	if (!SeriesBase::isNormal())
		return;

	ui.textBrowser->clear();	
	// ui.textBrowser->append(QStringLiteral("共有%1个序列>>").arg(ReadWorker::UID_Files.count()));
	ui.textBrowser->append(QStringLiteral("当前序列(%1):").arg(seriesUID));
	int w, h, s;
	SeriesBase::getSeriesSize(w, h, s);
	ui.textBrowser->append(QStringLiteral("宽:%1").arg(w));
	ui.textBrowser->append(QStringLiteral("高:%1").arg(h));
	ui.textBrowser->append(QStringLiteral("图像数:%1").arg(s));

	xyS->update(); xzS->update(); yzS->update();
	ui.graphicsView_XY->setSeries(xyS);
}
...
void DicomBrowse::on_comboBox_currentIndexChanged(int color)
{
 // 伪彩下拉菜单选择彩色后,设置序列彩色,并更新视图
	SeriesBase::setColor(color);
	ui.graphicsView_XY->updateView();
}
...
void DicomBrowse::on_lineEdit_ww_textEdited(QString ww)
{
    // 窗宽编辑框数字编辑完成后,设置序列窗宽位,并更新视图
	double ww_d = 0.0, wl = 0.0;
	SeriesBase::getWindow(wl, ww_d);
	ww_d = ww.toDouble();
	SeriesBase::setWindow(wl, ww_d);
	ui.graphicsView_XY->updateView();
}

void DicomBrowse::on_lineEdit_wl_textEdited(QString wl)
{
    // 窗位编辑框数字编辑完成后,设置序列窗宽位,并更新视图
	double ww = 0.0, wl_d = 0.0;
	SeriesBase::getWindow(wl_d, ww);
	wl_d = wl.toDouble();
	SeriesBase::setWindow(wl_d, ww);
	ui.graphicsView_XY->updateView();
}

void DicomBrowse::on_windowChanged()
{
	// 更新窗宽、窗位的编辑框数字
	double ww = 0.0, wl = 0.0;
	SeriesBase::getWindow(wl, ww);
	set_lineEdit_wl_text(wl);
	set_lineEdit_ww_text(ww);
}

// 修改该函数
void DicomBrowse::ReadCheckCompleted()
{
	...
	on_comboBox_which_currentIndexChanged(ui.comboBox_which->currentText());
}
...

运行调试,点击“选择Dicom文件夹”后

设置伪彩色

调整窗宽窗位:1)通过按住拖动鼠标右键;2) 修改窗宽窗位值;3) 按住鼠标左键划框(感兴趣区域);

至此,我们实现了XY视图切片浏览。

在收尾部分(下一章),将实现三视图,并提供工程项目的下载链接。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值