今回は、Qtについての備忘録です。 前回と同様、学習のアウトプット目的で書いています。 ご指摘等ありましたら、コメントに書いていただけると幸いです。

動作環境は以下の通りです。 【OS】Windows10 64bit 【CPU】Intel core i5-4590 【Qt】5.11.2 64-bit for Desktop (MSVC 2017) 【Qt Creator】4.7.1 (Community)

では、本題です。 [:contents]

* カスタムウィジェットとは <hr> Qtには既存のウィジェットに独自の機能を持たせた     カスタムウィジェット という機能があります。

基本的にカスタムウィジェットは、 既存のウィジェットから派生したクラスを登録することで扱えるようになります。

登録方法としては、二つ方法があります。

1. 格上げ 2. Custom Widget Plugin

1は比較的簡単で、環境もそのままで行えます。

2は難易度としては高いですが、より細かいカスタムが行えます。 しかしWindowsの場合、環境構築が大変です。

Pluginに関して、詳しくは以下をご覧ください。 ・[https://qiita.com/argama147/items/2d280a292a8c76ab9b5f:title] ・[https://doc.qt.io/qt-5/qtdesigner-customwidgetplugin-example.html:title] ・[https://stackoverflow.com/questions/16486366/how-do-i-create-a-custom-widget-plugin-for-qt-designer-with-cmake-and-visual:title]

今回は、1の格上げを用いることにします。

  • 格上げを用いてカスタムウィジェットを登録する<hr> 格上げにはQt Creatorを用いて行います。

まずは、カスタムウィジェットのクラスを作ります。

ここでつくるカスタムウィジェットは、 できるだけそのウィジェット内で完結するようにしたいので、 ボタンが押されている間、ボタンにcliked!!と表示 といった簡単なものにしたいと思います。

プロジェクトは、Qt ウィジェットアプリケーションです。 新規プロジェクトに以下のヘッダファイルを追加しています。

コードはこちら。

cpp

//custombutton.hpp #ifndef CUSTOMBUTTON_HPP #define CUSTOMBUTTON_HPP

#include

class CustomButton : public QPushButton { Q_OBJECT

public: CustomButton( QWidget* parent = nullptr ) : QPushButton( parent ) { connect( this, SIGNAL( pressed() ), this, SLOT( ChangeText() ) ) ; connect( this, SIGNAL( released() ), this, SLOT( ClearText() ) ) ; }

private slots: void ChangeText() { setText( “clicked!!” ) ; } void ClearText() { setText( “” ) ; } };

#endif // CUSTOMBUTTON_HPP

||< QPushButtonが押されたとき → ChangeText関数 QPushButtonが離されたとき → ClearText関数 といった具合にconnectしているだけです。

シグナルはスロットでキャッチできます。

次にこのクラスを登録してみましょう。 まずはプロジェクトの.uiファイルを開きます。 Qt Designerでもよいです。

次に基本クラスにあたるウィジェットを置き、その上で右クリックをします。 ここで格上げ先を指定…を選択。

[f:id:pit-ray:20181105002553j:plain]

表示されたダイアログに カスタムウィジェットの基本クラス カスタムウィジェットのクラス名 カスタムウィジェットクラスがあるヘッダファイル名 を入力し、追加します。 [f:id:pit-ray:20181105002556j:plain]

最後に選択し、格上げすればokです。 [f:id:pit-ray:20181105002559j:plain]

これで実行すると以下のような動作になります。 ここで注意したいのは、これは ウィジェットの機能としての動作 であるということです。 [f:id:pit-ray:20181105011848g:plain]

このように、派生クラスを登録することで、任意の機能を持たせたウィジェットを作れます。

ここで元のウィジェットが複数存在する場合は、どうしたらいいのでしょうか。 正直なところ、一つのウィジェットの中に複数のウィジェットがあるというのは、設計的に賛否がありそうです。 しかし、極めて密接に関係していて、そのウィジェットが複数ある場合、 同じクラス内に複数のウィジェットを配置して、内部でデータのやり取りをしたほうがよさそうです。その方がオブジェクト指向のカプセル化を実現できます。

  • 複数のウィジェットを扱うカスタムウィジェットをつくる<hr>

結論からいうと、 QWidgetを継承することで内部に複数のウィジェットを配置できます。 内部のウィジェットはポインタとして作成し、コード上でレイアウト等を決めます。

Qtのウィジェットは大抵、QWidgetクラスを継承しています。 QWidgetは例えるならば、画用紙のようなものです。

実際に.uiファイルや、Qt Designerで確認してみるとわかりますが、 Widgetオブジェクトは透明な枠だけのものです。

[f:id:pit-ray:20181105203950j:plain]

早速ですが、QPushButtonQLabelを用いて、カウンターを作りたいと思います。 ヘッダは以下のようになります。

cpp

//counter.hpp #ifndef COUNTER_HPP #define COUNTER_HPP

#include #include #include #include #include

class Counter : public QWidget { Q_OBJECT public: explicit Counter( QWidget* parent = nullptr ) : QWidget( parent ), button( new QPushButton( this ) ), label( new QLabel( this ) ), counter( 0 ) { InitLayout() ;

    //buttonからのclickedシグナルをこのクラスのIncrement()に接続
    connect( button, SIGNAL( clicked() ), this, SLOT( Increment() ) ) ;
}

~Counter() {
    delete button ;
    delete label ;
}

private: QPushButton* button ; QLabel* label ; int counter ;

void UpdateLabel() {
    label->setText( QString::number( counter ) ) ;
}

void InitLayout() {
    //縦方向のレイアウト生成
    QVBoxLayout* layout = new QVBoxLayout( this ) ;

    layout->addWidget( button ) ;
    layout->addWidget( label ) ;

    //ウィジェットを中央揃えにする
    layout->setAlignment( button, Qt::AlignCenter ) ;
    layout->setAlignment( label, Qt::AlignCenter ) ;

    setLayout( layout ) ;

    //ウィジェットのサイズを設定。
    //サイズはQt Designer, Qt Creatorのプロパティから確認できる。
    button->setMinimumSize( 180, 60 ) ;
    button->setText( "Increment" ) ;

    label->setMinimumSize( 180, 60 ) ;
    label->setAlignment( Qt::AlignCenter ) ;
    label->setFont( QFont( "Arial", 20, QFont::Bold ) ) ;
    UpdateLabel() ;
}

private slots: void Increment() { counter ++ ; UpdateLabel() ; } };

#endif // COUNTER_HPP

  <

このクラスは、基本クラスがQWidgetであるので、 格上げ元はWidgetオブジェクトにします。

ここで注意したいのは、レイアウトです。 この場合、配置はソースコード上で設定する必要があります。 レイアウトには様々な種類があり、目的に合ったものを選んでください。 ・[https://doc.qt.io/qt-5/layout.html:title] ・[https://doc.qt.io/qt-5/qt.html#AlignmentFlag-enum:title]

Layoutオブジェクトをつくり、setLayoutで渡します。 ここでは暗黙的にdeleteしています。 privateメンバとして保持し、コンストラクタでnew, デストラクタで明示的にdeleteでもよいです。 setAlignmentは第一引数は、内部ウィジェットのポインタです。 ・[https://doc.qt.io/archives/qt-4.8/qlayout.html#setAlignment:title]

以上のようにすることでこのように動作します。 [f:id:pit-ray:20181105203012g:plain]

このようにQWidgetを用いることで、 複数のウィジェットを組み合わせたカスタムウィジェットを作ることができます。

最初にも申しましたが、ご指摘は歓迎いたします。

今回の記事が参考になれば幸いです。 ご閲覧ありがとうございました。

  • 【参考文献】 (上で紹介したサイトは省略させていただきます) ・[https://stackoverflow.com/questions/8366617/combine-multiple-widgets-into-one-in-qt:title] ・[https://blog.qt.io/jp/2011/07/21/widget-propmotion/:title]