zoukankan      html  css  js  c++  java
  • Qt中截图功能的实现

    提要

    需求:载入一张图片并显示,能够放大缩小,能够截取图片的某个矩形并保存。

    原以为蛮简单的一个功能,事实上还是有点小复杂。

    最简单Qt图片浏览器能够參考Qt自带的Demo:Image Viewer Example

    看一下终于的实现效果:







    图片的载入显示

    这里须要实现一个QImageViewer的类。继承自QWidget。

    图片用QPixmap来载入和显示,还有三个成员各自是图片的缩放因子,图片是否已经载入,viewer是否已经初始化,是否处于裁剪状态。

    private:
    QPixmap m_pixmap;
    float scalling;
    bool isLoaded;
    bool isIntialised;
    bool isCropping;

    scalling值是用于记录图片的缩放比例。

    显示图片仅仅要又一次定义paintEvent,在里面绘制m_pixmap就能够了。

    void QImageViewer::paintEvent(QPaintEvent *event)
    {
    	QWidget::paintEvent(event);
    	if (m_pixmap.isNull())
    	{
    		return;
    	}
    
    	QPainter painter(this);
    	if (isLoaded)
    	{
    		painter.setRenderHint(QPainter::SmoothPixmapTransform);
    		QSize pixSize = m_pixmap.size();
    
    		//For canvas's size not change when window's size change.
    		if (!isInitialised)
    		{
    			QSize initialSize = event->rect().size();
    			scaling = 1.0 * initialSize.width() / pixSize.width();
    			isInitialised = true;
    		}
    		pixSize.scale(scaling * pixSize, Qt::KeepAspectRatio);
    		this->setMinimumSize(pixSize);
    
    		QPoint topleft;
    		topleft.setX((this->width() - pixSize.width()) / 2);
    		topleft.setY((this->height() - pixSize.height()) / 2);
    
    		painter.drawPixmap(topleft, m_pixmap.scaled(pixSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
    	}
    }

    截图

    思路非常easy。首先选择进入裁剪模式(能够在menu中加入一个action或者用快捷键),然后在图片上拖出一个矩形。最后按回车。截取完毕,Ctrl + s 保存。

    主要用到的是 QPixmap::copy(QRect rect) 方法。


    首先要实现一个CropRect类,用于记录截取的矩形。

    #ifndef CROPRECT_H
    #define CROPRECT_H
    #include <QPoint>
    #include <QPainter>
    
    class CropRect
    {
    public:
    	CropRect(){}
    	~CropRect(){}
    	void setStart(QPoint s) 
    	{ 
    		start = s; 
    	} 
    
    	void setEnd(QPoint e) 
    	{ 
    		end = e; 
    	} 
    
    	QPoint startPoint() const
    	{ 
    		return start; 
    	} 
    
    	QPoint endPoint() const
    	{ 
    		return end; 
    	} 
    	void reset()
    	{
    		QPoint P(0,0);
    		start = P;
    		end = P;
    	}
    
    	QSize& size() const
    	{
    		return QSize(width(), height());
    	}
    	
    	int height() const
    	{
    		return qAbs(startPoint().y() - endPoint().y());
    	}
    
    	int width() const
    	{
    		return qAbs(startPoint().x() - endPoint().x());
    	}
    
    private:
    	QPoint start;
    	QPoint end;
    };
    
    #endif // CROPRECT_H
    


    注意这里的start和end都是相对于当前要截取的图片的位置。也就是图片的左上角的坐标是CropRect的原点。


    接下来再QImageviewer中实现两个辅助的方法。

    由于图片并非恰好全然充满窗口,所以在设定裁剪框的时候,鼠标假设没有点在图片上,就应该不裁剪。所以首先应该推断屏幕中的某个点是否在图片上。

    bool QImageViewer::isContainPoint(QPoint &p)
    {
    	QSize s = m_pixmap.size();
    	s.scale(scaling * s, Qt::KeepAspectRatio);
    
    	//If pixmap bigger than current window.
    	if ((s.height() > this->rect().height()) && (s.width() > this->rect().width()))
    	{
    		return true;
    	}
    
    	QPoint topleft;
    	topleft.setX((this->width() - s.width()) / 2);
    	topleft.setY((this->height() - s.height()) / 2);
    
    	QRect rect(topleft, s);
    	return rect.contains(p);
    }

    第二个方法就是将鼠标的位置映射到图片上的位置,由于截图主要是对图片进行操作。

    图片的大小和窗口大小有四种情况,第一种是图片高度和宽度都大于窗口。


    逻辑就是将红色和绿色部分相加,得到点对于当前图片(已缩放)的位置。最后除以scalling就能够了。


    另一种是图片全然在窗口里面


    这样的情况将红色减去绿色部分,得到点对于当前图片(已缩放)的位置。最后除以scalling就能够了。还有两种简单的情况就不展开了,详细代码例如以下:

    QPoint QImageViewer::mapToPixmap(QPoint &screenPoint)
    {
    	QSize pixmapSize = m_pixmap.size();
    	pixmapSize.scale(scaling * pixmapSize, Qt::KeepAspectRatio);
    
    	//Get the position of screenPoint to the pixmap in show. 
    	QPoint tmpPos;
    	if (pixmapSize.width() > this->width() && pixmapSize.height() > this->height())
    	{
    		tmpPos.setX(pixmapSize.width() - (this->width() - screenPoint.x()));
    		tmpPos.setY(pixmapSize.height() - (this->height() - screenPoint.y()));
    	}
    	else if (pixmapSize.width() < this->width() && pixmapSize.height() > this->height())
    	{
    		tmpPos.setX(screenPoint.x() - (this->width() - pixmapSize.width()) / 2);
    		tmpPos.setY(pixmapSize.height() - (this->height() - screenPoint.y()));
    	}
    	else if (pixmapSize.width() > this->width() && pixmapSize.height() < this->height())
    	{
    		tmpPos.setX(pixmapSize.width() - (this->width() - screenPoint.x()));
    		tmpPos.setY(screenPoint.y() - (this->height() - pixmapSize.height()) / 2);
    	}
    	else{
    		QPoint topleft;
    		topleft.setX((this->width() - pixmapSize.width()) / 2);
    		topleft.setY((this->height() - pixmapSize.height()) / 2);
    		tmpPos.setX(screenPoint.x() - topleft.x());
    		tmpPos.setY(screenPoint.y() - topleft.y());
    	}
    	//return the position to the real pixmap.*/
    	return QPoint(tmpPos.x() / scaling, tmpPos.y() / scaling);
    }
    


    这里採取了一个投机取巧的办法。就是利用了QPoint.setX() 和 QPoint.setY()方法假设传进去的是负值,那么就等于传进去0,所以少了一些小于0的推断。


    接下来就是相应的鼠标事件,用于确定裁剪框的大小

    void QImageViewer::mousePressEvent(QMouseEvent *event)
    {
    	if ((event->buttons() == Qt::LeftButton) && isContainPoint(event->pos()) && isCropping)
    	{
    		cropRect.setStart(mapToPixmap(event->pos()));
    		cropRect.setEnd(mapToPixmap(event->pos()));
    		isStartingCrop = true;
    	}
    }
    
    void QImageViewer::mouseMoveEvent(QMouseEvent *event)
    {
    	if ((event->buttons() == Qt::LeftButton) && isStartingCrop)
    	{
    		if (isContainPoint(event->pos()))
    		{
    			cropRect.setEnd(mapToPixmap(event->pos()));
    			update();
    		}
    	}
    }
    
    void QImageViewer::mouseReleaseEvent(QMouseEvent *e)
    {
    	QRect rect(cropRect.startPoint(), cropRect.endPoint());
    	isStartingCrop = false;
    }
    


    裁剪框绘制的相关代码。这里也依据startpoint 和endpoint的相对位置,也有几种情况须要注意一下。

    更炫酷的动态蚂蚁线能够參考:Qt中绘制蚂蚁线

    		if (isCropping)
    		{
    			qDebug() << cropRect.width() << cropRect.height();
    			//painter.setPen(Qt::darkGreen);
    			QPen pen;
    			pen.setBrush(Qt::red);
    			pen.setStyle(Qt::DashLine);
    			pen.setWidth(1);
    			painter.setPen(pen);
    
    			//start point in the left to the end point.
    			if (cropRect.startPoint().x() < cropRect.endPoint().x())
    			{
    
    				if (cropRect.startPoint().y() < cropRect.endPoint().y())
    				{
    					//start point in the top to the end point.
    					painter.drawRect(topleft.x() + cropRect.startPoint().x() * scaling, topleft.y() + cropRect.startPoint().y() * scaling, cropRect.width() * scaling, cropRect.height() * scaling);
    				}
    				else{
    					//start point in the bottom to the end point.
    					painter.drawRect(topleft.x() + cropRect.startPoint().x() * scaling, topleft.y() + cropRect.endPoint().y() * scaling, cropRect.width() * scaling, cropRect.height() * scaling);
    				}
    			}
    			else
    			{
    				if (cropRect.startPoint().y() > cropRect.endPoint().y())
    				{
    					painter.drawRect(topleft.x() + cropRect.endPoint().x() * scaling, topleft.y() + cropRect.endPoint().y() * scaling, cropRect.width() * scaling, cropRect.height() * scaling);
    				}
    				else{
    					painter.drawRect(topleft.x() + cropRect.endPoint().x() * scaling, topleft.y() + cropRect.startPoint().y() * scaling, cropRect.width() * scaling, cropRect.height() * scaling);
    				}
    			}
    		}


    最后就是裁剪

    void QImageViewer::cropFinished()
    {
    	QRect crop(cropRect.startPoint(), QSize(cropRect.width(), cropRect.height()));
    	QPixmap cropped = m_pixmap.copy(crop);
    	m_pixmap = cropped;
    	cropRect.reset();
    	isCropping = false;
    	this->update();
    }

    打完收工。

  • 相关阅读:
    flask项目部署
    FastDFS分布式文件系统
    《app》团队冲刺二
    《APP》团队开发一
    软工二年级团队项目----自律小助手
    团队项目新闻app的需求分析
    团队成员及团队项目
    团队冲刺第八天
    团队冲刺第七天
    团队冲刺第六天
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5136797.html
Copyright © 2011-2022 走看看