- 從0到1,手把手帶你開發windows端的截圖軟體ScreenCap
- 當前版本:ScreenCap---001
- 支援全螢幕截圖
- 支援滑鼠拖動截圖區域
- 支援拖拽截圖
- 支援儲存全螢幕截圖
- 支援另存截圖到其他位置
- 注:博主所有資源永久免費,若有幫助,請點贊轉發是對我莫大的幫助
- 注:博主本人學習過程的分享,參照他人的文章皆會標註原作者
- 注:本人文章非盈利性質,若有侵權請聯絡我刪除
- 注:獲取資源或者諮詢問題請聯絡Q:2950319782
- 注:博主本人很菜,文章基本是二次創作,大佬請忽略我的隨筆
- 注:我會一步步分享實現的細節,若仍有問題聯絡我
- 倉庫master下的ScreenCap專案
- 若您無法正常存取,每次專案的資源會隨文章一同釋出,下載壓縮包即可,永久免費
- 壓縮包可能較GitHub更新不及時,請諒解
- win10系統
- 編譯器qtcreator4.11.1
- QT版本:5.14.2
- C++11
- 提供開始截圖的按鈕,點選開始截圖
- 在截圖介面提供右鍵選單選擇
- 選單實現儲存當前的截圖
- 儲存全螢幕截圖
- 截圖另存為
- 全螢幕截圖另存為
- 退出截圖
- 滑鼠可以拖拽截圖區域
- 圖片屬性實時計算
- 首先需要建立頁面ScreenCapWidget,提供開始截圖,按鍵設定,預設位置的按鈕
- 首先實現開始截圖的功能,這裡不能直接在視窗執行緒實現,需要單獨建立一個screenwidget類實現截圖的主要操作
- 獲取到screenwidget的範例後,應該處理截圖的邏輯了,建立範例的時候直接呼叫screenwidget父類別widget的showFullScreen函數,將screenwidget以全螢幕的方式顯示出來,整個螢幕是當前截圖的操作區域,遮擋其他操作,這裡我們重寫一下screenwidget的showEvent事件
- 而這個screenwidget類不應該一直存在,應該是呼叫開始截圖的時候才開始建立,這裡為了保證同一時刻只有一個screenwidget類建立,應該使用單例模式,確保只有一個範例
- screenwidget建立的時候不需要ui檔案,這裡我們只需要使用widget裡的繪圖事件和選單功能,自己使用程式碼實現
- 在標頭檔案裡首先維持一個靜態的QScopedPointer物件self,用於實現單例模式
- 定義一個公共的靜態介面Instance以實現其他類來生成screenwidget物件
- 下面來實現類的預設建構函式,提供選單功能,實現儲存當前的截圖,儲存全螢幕截圖,截圖另存為,全螢幕截圖另存為,退出截圖的功能
- 因為screencapwidget呼叫其fullShowScreen函數,這裡重寫showEvent函數
- showEvent函數中,直接獲取當前主螢幕的全螢幕影象儲存在fullScreen中,為提示使用者截圖開始了,這裡獲取到全螢幕物件後,模糊處理全螢幕,維持一個背景值bgScreen實現背景處理
- 截圖介面的互動邏輯等會再實現,先處理關鍵的部分,建立一個myscreen類,實現截圖實現的資料主要邏輯
- 重寫完showEvent後,已經獲取到全螢幕影象了,需要開始處理部分截圖了,即處理滑鼠事件,首先處理滑鼠按下press事件,第一次按下的位置就是起始位置,再根據此時myscreen的STATUS處理對應的事件
- 處理滑鼠移動的事件,如果還在myscreen還在選擇狀態,那麼移動完的位置就是截圖的結束位置,myscreen在移動狀態,那麼計算偏移量減去移動開始時候的起始位置movPos即可,將偏移量傳入myscreen的move函數中,計算move後的截圖區域
- 主要的滑鼠事件處理完了,下面處理release和右鍵事件
- 滑鼠事件處理完了之後,要截圖的影象的區域我們已經知道了,下面重寫paint事件
- 該類主要實現對截圖的資料計算,來給screenwidget重寫事件提供詳細的資料
- 這裡的類不需要視窗檔案,建立純粹的cpp類即可
- 需要獲得從screenwidget類傳入的qsize引數,這裡使用帶qsize引數的建構函式
- 首先截圖需要維護螢幕長和寬的值,maxHeight和maxWidth,這裡的資料應該是誰呼叫誰能獲取,全部設定為私有屬性,還需要設定其getWidth和getHeight方法
- 還需要維持截圖區域的左上角和右下角的point值leftUpPos和rightDownPos,並設定getLeftUp和getRightDown方法
- 處理滑鼠事件的時候,需要判斷當前截圖的狀態,維持列舉值STATUS,儲存選擇截圖區域,拖拽截圖,
- 這裡需要實現判斷滑鼠是否在現有的截圖區域內isInArea和計算移動後的截圖位置的move函數
注:關鍵程式碼只負責解釋各部分的邏輯關係,詳解看程式碼註釋
screencapwidget處理開始截圖的功能,建立screenwidget的唯一範例,並顯示全螢幕視窗
//ScreenWidget全螢幕顯示 ScreenWidget::Instance()->showFullScreen();
與showFullScreen相關的screenwidget的重寫showEvent事件
//重寫視窗被顯示的事件 void ScreenWidget::showEvent(QShowEvent *) { //設定初始位置 QPoint point(-1,-1); myscreen->setStart(point); myscreen->setEnd(point); //獲取當前螢幕物件 QScreen* pscreen = QApplication::primaryScreen(); //呼叫QScreen的grabwindow進行全螢幕截圖 *fullScreen = pscreen->grabWindow(0,0,0,myscreen->getWidth(),myscreen->getHeight()); //設定透明度實現模糊背景 QPixmap pix(myscreen->getWidth(),myscreen->getHeight()); pix.fill((QColor(160,160,160,200))); bgScreen = new QPixmap(*fullScreen); QPainter p(bgScreen); p.drawPixmap(0,0,pix); }
screenwidget實現單例模式的主要程式碼
//定義單例模式,確保截圖的時候只能有一個 ScreenWidget* ScreenWidget::Instance() { //還沒有建立範例 if(self.isNull()) { //加把鎖,只能有一個執行緒存取 static QMutex mutex; //自動加解鎖 QMutexLocker locker(&mutex); //再次判斷有沒有範例,防止等待的時間中有執行緒獲取到範例了 if(self.isNull()) { self.reset(new ScreenWidget); } } return self.data(); }
screenwidget提供的選單功能
//建立一個選單檔案 menu = new QMenu(this); //新增選單的功能 menu->addAction("儲存當前的截圖",this,SLOT(saveScreen())); menu->addAction("儲存全螢幕截圖",this,SLOT(saveFullScreen())); menu->addAction("截圖另存為",this,SLOT(saveScreenOther())); menu->addAction("全螢幕截圖另存為",this,SLOT(saveFullOther())); menu->addAction("退出截圖",this,SLOT(hide()));
screenwidget維持myscreen的類,並在screenwidget的建構函式中範例化myscreen類,傳入當前螢幕的大小,二者同步生成
myScreen* myscreen;
//獲取螢幕大小 myscreen = new myScreen(deskGeometry.size());
獲取到當前螢幕的qrect物件,呼叫size函數獲取螢幕的size值,使用宏展開式,不單獨處理了,需要的時候直接綻開計算
#define deskGeometry qApp->primaryScreen()->geometry()
處理圖片移動
void myScreen::move(QPoint p) { //計算move後的四個點座標 int lx = leftUpPos.x() + p.x(); int ly = leftUpPos.y() + p.y(); int rx = rightDownPos.x() + p.x(); int ry = rightDownPos.y() + p.y(); //確保移動後的截圖不會超出螢幕範圍 if(lx < 0) { lx = 0; rx -= p.x(); } if(ly < 0) { ly = 0; ry -= p.y(); } if(rx > maxWidth) { rx = maxWidth; lx -= p.x(); } if(ry > maxHeight) { ry = maxHeight; ly -= p.y(); } //更新移動後的值 leftUpPos = QPoint(lx,ly); rightDownPos = QPoint(rx,ry); startPos = leftUpPos; endPos = rightDownPos; }
處理滑鼠press
void ScreenWidget::mousePressEvent(QMouseEvent *e) { int status = myscreen->getStatus(); //選擇區域的狀態 if(status == myScreen::SELECT) { //把滑鼠按下的位置設定為開始位置 myscreen->setStart(e->pos()); } //拖拽截圖 else if(status == myScreen::MOV) { //滑鼠不在截圖的區域內,是要重新選擇截圖區域 if(myscreen->isInArea(e->pos()) == false) { //新按下的位置設定為開始位置,並重置狀態為選擇 myscreen->setStart(e->pos()); myscreen->setStatus(myScreen::SELECT); } //在截圖區域內,是要拖拽截圖 else { //開始移動的起始位置就是現在滑鼠按下的位置 movPos = e->pos(); this->setCursor(Qt::SizeAllCursor); } } this->update(); }
處理滑鼠move
void ScreenWidget::mouseMoveEvent(QMouseEvent *e) { //在選擇狀態 if(myscreen->getStatus() == myScreen::SELECT) { myscreen->setEnd(e->pos()); } //在移動狀態 else if(myscreen->getStatus() == myScreen::MOV) { //計算滑鼠偏移量 QPoint p(e->x() - movPos.x(),e->y() - movPos.y()); myscreen->move(p); movPos = e->pos();//儲存上一次滑鼠的位置 } //觸發視窗的更新,重新繪製螢幕截圖和矩形框 this->update(); }