Turn On Blog

ぺーぺーSEの色々メモ

【Qt】スクリーンショット作成アプリケーションを製作する no.4 スクリーンショットの範囲指定切り出し

no.1 : 【Qt】スクリーンショット作成アプリケーションを製作する その1 - TRN
no.2 : 【Qt】スクリーンショット作成アプリケーションを製作する no.2 - TRN
no.3 : 【Qt】スクリーンショット作成アプリケーションを製作する no.3 現在日時をもとにファイル名を生成する - TRN
やったーできたぞー
f:id:taurano:20141228192408p:plain

今回やること

  • スクリーンショットの範囲を指定したい
    • ディスプレイ上のすべてが半透明になる
    • そのうえでマウスをドラッグすると、範囲が指定できる
    • ドラッグを離すと、勝手にファイルが作成されている

これができたらひとまず完成となります。

つくるもの

  • ScreenShot

ScreenShot

責務
実現方法

指定範囲は、以下の図のように描画します。
f:id:taurano:20141228170924p:plain
範囲指定開始の指示を受けると、まずその時点での画面キャプチャ(図中pixmap_)を保存します。
そして画面キャプチャを描画したフルスクリーンのウィンドウ(図中widget)を表示し、
その上に範囲指定部分をくりぬいた半透明のオブジェクト(図中path)を描画します。

ScreenShot.h
#ifndef SCREENSHOT_H
#define SCREENSHOT_H

#include <QDialog>
#include <QMouseEvent>

class ScreenShot : public QDialog
{
	Q_OBJECT
private:
	QPixmap pixmap_;
	QPoint start_;
	QPoint end_;
	
public:
	explicit ScreenShot(QWidget *parent = 0);
	
public slots:
	void shot();
	
protected:
	// 描画
	void paintEvent(QPaintEvent *);
	
protected:
	// ドラッグ検知
	void mouseMoveEvent(QMouseEvent *e);
	void mousePressEvent(QMouseEvent *e);
	void mouseReleaseEvent(QMouseEvent *e);
	
private:
	void takeScreenShot();
};

#endif // SCREENSHOT_H

pixmap_は、描画開始時点の画面キャプチャを示します。
start_は指定範囲の左上、end_は指定範囲の右下を示します。

ScreenShot.cpp
#include "screenshot.h"

#include <QApplication>
#include <QDesktopWidget>
#include <QGuiApplication>
#include <QScreen>
#include <QPainter>
#include <QDebug>
#include "save.h"

ScreenShot::ScreenShot(QWidget *parent) :
	QDialog(parent)
{
}

void ScreenShot::shot()
{
	// 現在のスクリーンショットを撮影
	pixmap_ = QGuiApplication::screens().at(0)->grabWindow(QApplication::desktop()->winId());

	// スクリーン切り出し範囲を初期化
	start_ = QPoint(0, 0);
	end_ = QPoint(0, 0);
	
	// フルスクリーンでウィンドウを表示
	this->setWindowState(Qt::WindowFullScreen);
	this->show();
}

void ScreenShot::paintEvent(QPaintEvent *)
{
	// 線なしペインタを生成
	QPainter painter(this);
	painter.setPen(Qt::NoPen);
	
	// スクリーンの大きさを取得
	QRect screenRect = QApplication::desktop()->screen(0)->rect();
	
	// 全体スクリーンショット画像を自身に描画
	painter.drawPixmap(screenRect,pixmap_);
	
	// 選択した範囲以外を塗りつぶすパスを作成(ちょっと角丸)
	QPainterPath path;
	path.addRect(screenRect);
	path.addRoundedRect(QRect(start_, end_), 8, 8);
	
	// 選択した範囲以外を塗りつぶすパスを描画
	painter.setBrush(QBrush(QColor(0,0,0,100)));
	painter.drawPath(path);
}

void ScreenShot::mouseMoveEvent(QMouseEvent *e)
{
	// マウスが動くたびに再描画かける必要あり
	end_ = e->pos();
	repaint();
}

void ScreenShot::mousePressEvent(QMouseEvent *e)
{
	// クリックされたら範囲指定開始。始点を設定する
	start_ = e->pos();
}

void ScreenShot::mouseReleaseEvent(QMouseEvent *e)
{
	// マウスがドラッグを終了したら範囲指定終了。
	end_ = e->pos();
	takeScreenShot();
}

void ScreenShot::takeScreenShot()
{
	// 全体スクリーンショットから指定範囲をコピーする
	QPixmap pixmap = pixmap_.copy(QRect(start_, end_));
	
	// 保存する
	Save::save(pixmap);
	
	// windowを閉じる。
	this->close();
}
shot

スクリーンショット範囲指定開始時に呼ばれる関数です。
その時点での画面キャプチャを撮影し、指定範囲を初期化します。

paintEvent

再描画の必要が生じる都度呼ばれます。
shot()内で撮影したスクリーンショットを自分自身の全範囲に描画し、
その上から選択範囲以外を塗りつぶすパスを描画します。
path.addRoundedRectとしてるのは、角丸だとちょっとオシャレだからです。
f:id:taurano:20141228174550p:plain
こんな感じになる。

mousePressEvent

マウスボタンが押下された瞬間に呼ばれるコールバック関数です。
指定範囲の開始地点をQMouseEventのpos()関数で取り出した地点に設定します。

mouseMoveEvent

マウスがボタン押下状態でドラッグされたときに呼ばれるコールバック関数です。
指定範囲の終了地点を変更しながら、ユーザがリアルタイムに指定範囲を把握できるようにrepaint()による再描画をかけています。

mouseReleaseEvent

マウスがボタン押下解除されたときに呼ばれます。
指定範囲の終了地点を設定し、スクリーンショット切り出しを呼び出します。

takeScreenShot()

はじめに撮影していた画面キャプチャから、copy()関数を使って指定範囲部分のみを切り出します。
Save::save()に切り出したpixmapをわたし、保存を依頼します。
最後に全画面表示を解除し、終了となります。

まとめ・課題

とりあえずブログを書くのに必要そうなスクリーンショット撮影ツールが作成できました。やったー!
全部で2~3時間程度で作成することができました。ライン数はたぶん0.3KLくらい。
Qtのライブラリの強力さと便利さが伝わると嬉しいです。

課題としては、以下を今後実装していきたいです。

  • ファイル名を連番で付ける仕組み
    • すべてのファイルで共通の連番を付ける→リセットも必要
    • 指定された日付・時刻etcの書式ごとに連番を付ける
  • 範囲指定 終了地点が開始地点より左または上に来てもちゃんとうごくようにする

以上です。お付き合いいただきありがとうございました。
ソースはGitHubとかに置いて公開するといいんでしょうか。またやってみたいと思います。