質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.76%

  • C++

    3256questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Qt

    124questions

    QtはGUIプログラムの開発で広く使われているクロスプラットフォーム開発のフレームワークです。

[Qt]Qtのイベント操作

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 414

BeatStar

score 921

(趣味で)C++でやっています。

C++ではGUIはQtが楽らしいので使ってみました。

以前質問したDLL云々は何とか解決しました。

QtCreator付属のヘルプ ( "ヘルプ(H)" のやつ )や

他の方のPDFを見ながら挑戦しています。

私個人はなんとなく扱いにくいので

せめてJavaのSwing風 ( なんかもう一方あった気がしますが... )にできないかなと思い、

DLL化しようと思いました。

大雑把な考えとしては

まずDLL及び実行ファイルで呼びだすインターフェースクラス ( IEventLinstener, IButton etc. ) を定義し、

IButtonをDLL内で継承して Factoryパターンを適用して CreateButtonInstance関数 ( もちろんオープン。 )で生成する。

IEventListener は 実行ファイル側で実装し、CreateButtonInstance関数に渡して、WButton ( IButton実装 ) のコンストラクタに渡して生成&登録。

という感じです。

読みにくいので...

[ DLL内 ]
1. IEventListener定義
2. IButton定義
3. IButtonを実装したWButtonを定義
4. IButtonとして返す CreateButtonInstance関数を定義 ( opened )

[実行ファイル]
1. IEventListener定義
2. IButton定義
3. IEventListenerを実装したイベント系クラスを定義
4. Windows API の動的リンクを用いて CreateButtonInstance等を使えるようにする
5. CreateButtonInstanceに 3のオブジェクト等の情報を渡して IButton型のオブジェクト ( 中身は WButton ) を生成する

という感じです。

まずは動かないと意味がないのでDLL化する前に そもそものQtの使い方を学び、どうやったら動くか試してみました。

継承もせずにコンポジションもせずにそのまま使う方法だと

// インクルード等はしてあるものとする。
int main( int argc, char* argv[] ){
    QApplication app( argc, argv );

    // メインウィンドウ用?
    QWidget* window = new QWidget();

    // メインウィンドウのサイズ等の変更
    window->resize( 500, 600 );
    window->move( 10, 10 );

    QPushButton* button1 = new QPushButton( "button1" );

    button1->resize( 20, 100 );
    button1->move( 10, 10 );

    button1->setParent( window ); // button1を windowに登録

    window->show();

return app.exec();    
}

だとわかりました。( もしかするとQWidgetは QMainWindowとかみたいな別のクラスになるかもしれないが。 )

( もちろん、deleteする必要があると思いますが。 サンプルなので。 )

Windows API だと ウィンドウプロシージャに相当するやつがあるはずなので参考にしたPDFを見ると

QObject::connect非メンバ関数でやるらしいので

// イベントリスナーインターフェース。実行ファイルにも公開するつもり。
// Windows API で言えば ウィンドウプロシージャに相当。
// コントロールごとに登録するメンバ関数が違う?
class IEventListener{
      public:
                 IEventListener(){}

                 virtual void buttonClicked( void ) = 0;
};

// BUTTON用インターフェース。
class IButton{
      public:
                 virtual ~IButton(){}
                 virtual std::string getText( void ) = 0;
                 virtual bool        setText( const std::string text ) = 0;
};


// 実際のButtonクラス。内部にQPushButtonを持ち、イベントinterfaceと親ウィンドウをコンストラクタで引き受けて
// イベントの設定も行う。
class WButton : public IButton, public QWidget{
      public:
                 WButton( QWidget* window = nullptr, IEventListener *e = nullptr ) : event(e){
                          button1 = new QPushButton("button1");
                          button1->setParent( window );

                          if( e != nullptr ){
                              QObject::connect( button1, SIGNAL( clicked() ), this, SIGNAL( PushButtonMessage() ) );
                          }
                 }

                 std::string getText( void ){

                 }

                 bool setText( const std::string text ){

                 }

      signals:
                 void PushButtonMessage( void ){
                      event->buttonClicked();
                 }
      private:
                 QPushButton* button1;
                 IEventListener* event;
};


// ----------------------------- //

// ボタン用イベントクラス。実際には実行ファイル側で定義する予定。
class ButtonEvent : public IEventListener{
      public:
                ButtonEvent(){}
                ~ButtonEvent(){}

                void buttonClicked( void ){
                     MessageBox( NULL, "button1 clicked", "", MB_OK );
                }
      protected:
      private:

};

// --------- //


int main( int argc, char *argv[] ){
    QApplication app( argc, argv );

    QWidget* window = new QWidget();
    window->resize( 500, 600 );

    IEventListener* event = new ButtonEvent();

    IButton* Button1 = new WButton( window, event );

    window->show();

return app.exec();
}

のようにしてみました。

コンパイルは通りましたが、私の予想結果は

"button1" ボタンが押された -> "button1 clicked"というメッセージボックスの表示

という感じなのですが、実際には "button1" ボタンが押されても無反応です。
( 空の関数を実行したっていう感じの。 )

出来れば QPushButtonを実行ファイルに見せずに イベント登録や基本的な動きをDLLにして、

実行ファイルは 実際に行うイベントを定義して、ifstream, ofstreamみたいに使うだけ...

という風にしたいのですが...

一応自分なりに思いついたものは試してみましたがうまくいかないのです。

[情報]
言語: C++
コンパイラ: MinGW

宜しくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

 とりあえずエラーを消す

Windows10 + Visual Studio 2015 + Qt5.6.0 で試してみました。
質問者様のプログラムを実行すると、次のようなログが出力されました。

QObject::connect: No such signal QWidget::PushButtonMessage()

どうやら、PushButtonMessageの在り処がQWidgetであると勘違いしているようです。


次のようにすれば、明示的にクラスを指定することが出来ます。

QObject::connect( button1, &QPushButton::clicked, this, &WButton::PushButtonMessage);

このように書き換えたところ、私の環境では正常に動作しました。

 根本原因は何か

Qtはなかなか癖が強く、私も以前扱っていたときに四苦八苦した記憶があります。
次のような点に気を付けてコーディングすれば、かなりのエラーを防げるかと思います。

  • 可能な限り全てのクラスでQObjectを継承する
    公式リファレンスで言及されていた気がします。
  • QObjectを継承したクラスの定義部先頭に、Q_OBJECTマクロを書く
    必須です。これがないと、signal/slotの恩恵に預かれません。

Notice that the Q_OBJECT macro is mandatory for any object that implements signals, slots or properties. 引用元

  • クラスの定義と実装を分ける
    確かQtのバグか何かで、Q_OBJECTマクロと一部の実装を分けないと正常動作しません。
  • クラス間のやり取りはsignal/slot
    マルチスレッド化するときに便利です。それに、メッセージパッシングってよさげ。
  • 出来るだけQtに予め用意されているクラスを用いる
    出力はqDebug、文字列はQString。他にもあるかもしれません。
  • メモリはヒープ領域に確保
    Qt系のクラスは、メモリリークを防ぐ仕組みを持っているらしいです。

When QObjects are created on the heap (i.e., created with new), a tree can be constructed from them in any order, and later, the objects in the tree can be destroyed in any order. When any QObject in the tree is deleted, if the object has a parent, the destructor automatically removes the object from its parent. If the object has children, the destructor automatically deletes each child. No QObject is deleted twice, regardless of the order of destruction. 引用元

  • 多重継承を出来るだけ避ける
    これはQtの注意点というより、C++の注意点ですね。細心の注意が必要となります。
  • 困ったらリビルド
    mocのゴミが邪魔をすることがあるので、リビルドで急にバグが消えたりします。

ちょっとうろ覚えなんですが、私の経験則ではなく、何かしらソースがあります。
大体公式リファレンスだったはず。

 適当に組んでみた

SwingやAWTを割と意識してますが、こっちもうろ覚えだったりします。

/* JButton.hpp */
#pragma once
#include <QtCore/QObject>

class QPushButton;

namespace sw_like {
    class ActionListener;

    class JButton : public QObject {
        Q_OBJECT

    private:
        QPushButton *button_;

    public:
        JButton(void) = delete;
        JButton(QString *label, QWidget *parent = Q_NULLPTR);

        ~JButton(void) {}

        void addActionListener(ActionListener *);
    };
}
/* JButton.cpp */
#include "JButton.hpp"
#include "ActionListener.hpp"

#include <QtCore/QString>
#include <QtWidgets/QPushButton>

sw_like::JButton::JButton(QString *label, QWidget *parent)
    : button_(new QPushButton(*label)) {

    this->button_->setParent(parent);
}

void sw_like::JButton::addActionListener(sw_like::ActionListener *listener) {
    QObject::connect(button_, SIGNAL(clicked()),
                     listener, SLOT(actionPerformed()));
}
/* ActionListener.hpp */
#pragma once
#include <QtCore/QObject>

namespace sw_like {
    class ActionListener : public QObject {
        Q_OBJECT

    public:
        ActionListener(void) {};
        virtual ~ActionListener(void) {}

    public slots:
        virtual void actionPerformed(void) = 0;
    };
}
/* header.hpp */
#pragma once
#include "ActionListener.hpp"
#include <QtCore/QObject>

namespace ls {
    class ButtonListener : public sw_like::ActionListener {
        Q_OBJECT

    public:
        ButtonListener(void) {}
        ~ButtonListener(void) {}

    public slots:
        void actionPerformed(void) override;
    };
}
/* main.cpp */
#include "header.hpp"
#include "ActionListener.hpp"
#include "JButton.hpp"

#include <QtCore/QDebug>
#include <QtCore/QString>
#include <QtWidgets/QApplication>
#include <QtWidgets/QDialog>

void ls::ButtonListener::actionPerformed(void) {
    qDebug() << "Button";
}

int main(int argc, char **argv) {
    QApplication app(argc, argv);
    QDialog *myDialog = new QDialog();

    auto *myButton = new sw_like::JButton(new QString("click"), myDialog);
    auto *buttonListener = new ls::ButtonListener();

    myButton->addActionListener(buttonListener);

    myDialog->show();
    return app.exec();
}

sw_like::JDialogを作らなかったのは手抜きです。すみません。
(だいたい)仕様通りに動作しますが、参考程度にお願いします。

...メモリリークが本当に起きていないかは、ちょっと自信がありません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/09/09 18:36

    ご回答ありがとうございます!

    なるほど。

    クラス名::メンバ関数名

    で明示するのですか。

    今外出先なので、帰宅後試してみます!

    キャンセル

  • 2017/09/09 18:39

    割と荒技感があるので、Q_OBJECTマクロを上手く使えるように設計を見直した方がいいと思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.76%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 解決済

    c++ 関数ポインタについて

    シミュレータを使ってシミュレーションを行っているのですが、そのシミュレータのプログラムについての質問です。 このシミュレータでは、処理する内容をシミュレーションイベントとして

  • 解決済

    PyQt4におけるシグナル/スロットについて。無限ループしてしまう。

    毎度のことお世話になっております。 現在、python3.4言語でPyQt4を学んでいます。 本当ならPyQt4の書籍で学びたいのですが、 今現在、日本語によるものがない

  • 解決済

    C++ デフォルトコンストラクタについて

    現在、C++の参考書のデフォルトコンストラクタの項目でつまづいています。 以下、参考書からの抜粋 ========== デフォルトコンストラクタは自分で定義しなければ勝手

  • 解決済

    win32apiのGetCommandLine()でコマンドライン引数がうけとれない

    発生している問題・エラーメッセージ win32apiのGetCommandLine()でコマンドライン引数をうけとると "C:Users\ユーザー名\documents\v

  • 解決済

    Xamarinのコントロールの名前の取得

    Xamarinのコントロールの名前の取得 XamarinのButtonコントロールが押されたとき、 押されたButtonコントロールの名前を取得する方法を教えてください。

  • 解決済

    [Qt]Qtをクラス化する

    C++でやっています。 Qtを使ってみたいと思い、挑戦中です。 なにやら面倒くさいので Qtを簡略化するDLLを作成しようとしています。 ですが、Qtそのものを使い切れ

  • 解決済

    GtkmmのtextViewのカーソルの位置を移動させたい

    gtkmmを使って、テキストエディタを作ろうとしています。 textViewウィジェットのカーソルの位置をコマンドで移動できるようにしたいのですが、メソッドでカーソル位置を移動

  • 解決済

    JavaFXでbuttonの色をデフォルトに戻すには?

    JavaFXでボタンを押されたら色が変わる処理を書いています。 ボタンが押されたら色が変わり、もう一度押されたら元のデフォルトの色に戻したいのですが、方法が分かりません。デフォルト

同じタグがついた質問を見る

  • C++

    3256questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Qt

    124questions

    QtはGUIプログラムの開発で広く使われているクロスプラットフォーム開発のフレームワークです。