Qt勉強会@名古屋 #27 参加したので

qt-users.jp


Qt勉強会@名古屋に参加して、ふむふむした内容を雑にまとめます。

ubuntu 18.04.2でやっています。

・環境構築

www.qt.io


こちらqt.ioから

Download. Try. Buy. -> go open source -> download

を選択。

Downloadディレクトリにqt〜〜.runファイルがあるので

chmod +x qt〜〜.run

で実行権を与え

sudo qt〜〜.run

とかってやると、インストーラが起動するので従います。
これで、qtのいろいろがDLされます。

・Qt
QtCreatorと呼ばれる、Qtに特化してる(と思う)統合開発環境を使います。
QtCreator、Qt使わなくてもいけちゃうので普段遣いにも良さそうです。かなり動きが軽くサクサク動く印象があります。

左の「ようこそ」 -> +新しいプロジェクト
でプロジェクト作成画面へ。

今回、ウィジェットの説明を勉強会中に聞いたので「Qtウィジェットアプリケーション」を選択。
後は適当に選びます。

Formsフォルダに「.ui」ファイルが入っていますが、これを選んで左の「デザイン」を選ぶとGUIでグリグリとパーツ配置ができます。

ソースが自動生成されているかと思いますが、

ui->setupUi(this);

みたいなソースがあるので、この辺りのコードで何かいい感じに読み込んでくれるのでしょう。


・今回作ったものと知見

これをC++/Qtで作ってました。


Widgetへの画像表示
.uiのデザイン画面で、Graphic Viewがあるので、それを適当に貼り付けます。
右上から貼ったGraphic Viewを選び、右下でobject nameをわかりやすい感じに変更します。

.proに

RESOURCES += \
        画像ディレクトリ

とかって追加すると、左のプロジェクト一覧に画像が追加されていい感じになります。

自動生成したソースだと、MainWindowクラス(名前は違うかも)のインスタンスを作り、showメソッドを実行しているかと思います。
このMainWindowにゴリゴリ書いていきます。

    ui->setupUi(this);
    ui->forwardview->setScene(&forwardScene);
    ziki = new QImage();
    ziki->load(ファイルパス);
    Qziki=QPixmap::fromImage(*ziki);
    forwardScene.addPixmap(Qziki);

※
forwardviewはGraphic Viewの名前
forwardSceneはQGraphicsSceneの名前
zikiはQImage*の名前
QzikiはQPixmapの名前

これで画像表示ができるはずです。
QPixmapに一度変換するのがミソでしょうか。

・画像加工
QImage自体は、(多分)ただの画素データの集合なので、直接弄ったり、用意されているメソッドで加工できます。

・大きさ変更

*ziki=ziki->scaled(ZIKI_SIZE_W,ZIKI_SIZE_H,Qt::KeepAspectRatio);

※
ZIKI_SIZE_W、ZIKI_SIZE_Hは#define定数

・反転
こちらはメソッドがないぽかったので、作りました。

QImage MainWindow::reverseQImage(QImage Qi){

    QImage *retQImage;
    *etQImage=Qi;

    for(int i=0;i<ZIKI_SIZE_H;i++){
        for(int j=0;j<ZIKI_SIZE_W;j++){
            QRgb tmpRGB=Qi.pixel(ZIKI_SIZE_W-j-1,i);
            retQImage->setPixel(j,i,tmpRGB);
        }
    }

    return *retQImage;
}

こんな感じで、pixelで取得してsetPixcelで設定すれば加工できます。
QImageのサイズには注意です。
QImage::pixel: coordinate (50,90) out of range
って怒られます。

・キー取得
eventFilter使いました。

protected:
    bool eventFilter(QObject* widget, QEvent* event);

と宣言します(protectedでないとダメらしい)

bool MainWindow::eventFilter(QObject * obj, QEvent * event)
{
    QKeyEvent * keyEvent;

    //キーが押されている時のイベントの処理
    if(event->type() == QEvent::KeyPress){
        処理内容
    }
}

とかって書き、MainWindowのコンストラクタらへんに

    installEventFilter(this);

と書くと、キー入力を受け取れるようになります。

・画像の移動
Graphic Viewの好きなとこにQPixmapを貼りたかったのですが失敗。
中央寄せ、左寄せ...など、上下左右中央に寄せて表示しかできなかったです。
なので、

①Graphic View自体を動かす
②でかいQImageの背景を作り、そこにゴリゴリ書いていく

の2つを考え②を採用しました。

・詳細

QImage MainWindow::reverseQImage(QImage Qi){

    QImage *retQImage;
    retQImage=new QImage(QSize(1000,1000),QImage::Format_RGBA8888);

    for(int i=0;i<ZIKI_SIZE_H;i++){
        for(int j=0;j<ZIKI_SIZE_W;j++){
            QRgb tmpRGB=Qi.pixel(ZIKI_SIZE_W-j-1,i);
            retQImage->setPixel(j+zikiX,i+zikiY,tmpRGB);
        }
    }

    return *retQImage;
}

さっきの反転プログラムを変えました。
QImageを作る際にQSize(1000,1000)を指定して、大きいサイズのQImageを作ります。
Format_RGBA8888はよくわかってないです(宿題)。
zikiXとzikiYに座標情報を入れ、さっきのeventFilter実行時に値を変えてやります。

・定期的な実行
ここでQtのsignal、slotを使います。

    cycleTimer->setInterval(100);
    cycleTimer->setSingleShot(false);
    connect(cycleTimer,SIGNAL(timeout()),
            this,SLOT(slotCycleEvent()));
    cycleTimer->start();

※
cycleTimerはTimerの名前

setIntervalとすることで、Nミリ秒ごとにtimeoutシグナルを発行するようになります。
setSingleShotをtrueにすると一回のみになります。
このTimerクラスのtimeoutシグナルと、描画処理のあるスロットをconnectして、タイマーを走らせば定期処理の完成です。

とても簡単でいいですね!!

あとは描画処理に

    for(int i=0;i<1000;i++){
        for(int j=0;j<1000;j++){
            Dziki->setPixel(j,i,0);
        }
    }
    *Dziki=reverseQImage(*ziki);
    Qziki=QPixmap::fromImage(*Dziki);
    forwardScene.clear();
    forwardScene.addPixmap(Qziki);

※
DzikiはQImage*の名前

と書きます。
zikiには、読み込んだ画像データが入っています。
それを0埋めした*Dzikiに入れ、QPixmapに変換します。
forwardScene.clear()でwidgetの描画を消し、addPixmapで描画します。


以上で、動く船の完成です。


・やり残し
・もっと簡単な方法がありそう
QImage::pixel: coordinate (N,N) out of range
のお怒りが毎ミリ秒ごとに流れてるので、どこか直す
・押したキーの種類を受け取って、戦艦を上下左右に動かす。


・最後に
www.irasutoya.com

今回、イラスト屋の素材を借りました。
統一感を出すため、イラスト屋オンリーのシューティングゲームを作るのが目標です。

来週の勉強会では、少しでもQtのスキルアップがしたいですね。(わからないことがあれば、#qtjpに投げるので、よろしくお願いします。)