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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Qt

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

C++

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

Q&A

解決済

1回答

1922閲覧

[Qt]Qtのイベント操作

BeatStar

総合スコア4958

Qt

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

C++

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

0グッド

0クリップ

投稿2017/09/09 06:16

(趣味で)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の使い方を学び、どうやったら動くか試してみました。

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

C++

1// インクルード等はしてあるものとする。 2int main( int argc, char* argv[] ){ 3 QApplication app( argc, argv ); 4 5 // メインウィンドウ用? 6 QWidget* window = new QWidget(); 7 8 // メインウィンドウのサイズ等の変更 9 window->resize( 500, 600 ); 10 window->move( 10, 10 ); 11 12 QPushButton* button1 = new QPushButton( "button1" ); 13 14 button1->resize( 20, 100 ); 15 button1->move( 10, 10 ); 16 17 button1->setParent( window ); // button1を windowに登録 18 19 window->show(); 20 21return app.exec(); 22}

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

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

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

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

C++

1// イベントリスナーインターフェース。実行ファイルにも公開するつもり。 2// Windows API で言えば ウィンドウプロシージャに相当。 3// コントロールごとに登録するメンバ関数が違う? 4class IEventListener{ 5 public: 6 IEventListener(){} 7 8 virtual void buttonClicked( void ) = 0; 9}; 10 11// BUTTON用インターフェース。 12class IButton{ 13 public: 14 virtual ~IButton(){} 15 virtual std::string getText( void ) = 0; 16 virtual bool setText( const std::string text ) = 0; 17}; 18 19 20// 実際のButtonクラス。内部にQPushButtonを持ち、イベントinterfaceと親ウィンドウをコンストラクタで引き受けて 21// イベントの設定も行う。 22class WButton : public IButton, public QWidget{ 23 public: 24 WButton( QWidget* window = nullptr, IEventListener *e = nullptr ) : event(e){ 25 button1 = new QPushButton("button1"); 26 button1->setParent( window ); 27 28 if( e != nullptr ){ 29 QObject::connect( button1, SIGNAL( clicked() ), this, SIGNAL( PushButtonMessage() ) ); 30 } 31 } 32 33 std::string getText( void ){ 34 35 } 36 37 bool setText( const std::string text ){ 38 39 } 40 41 signals: 42 void PushButtonMessage( void ){ 43 event->buttonClicked(); 44 } 45 private: 46 QPushButton* button1; 47 IEventListener* event; 48}; 49 50 51// ----------------------------- // 52 53// ボタン用イベントクラス。実際には実行ファイル側で定義する予定。 54class ButtonEvent : public IEventListener{ 55 public: 56 ButtonEvent(){} 57 ~ButtonEvent(){} 58 59 void buttonClicked( void ){ 60 MessageBox( NULL, "button1 clicked", "", MB_OK ); 61 } 62 protected: 63 private: 64 65}; 66 67// --------- // 68 69 70int main( int argc, char *argv[] ){ 71 QApplication app( argc, argv ); 72 73 QWidget* window = new QWidget(); 74 window->resize( 500, 600 ); 75 76 IEventListener* event = new ButtonEvent(); 77 78 IButton* Button1 = new WButton( window, event ); 79 80 window->show(); 81 82return app.exec(); 83} 84

のようにしてみました。

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

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

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

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

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

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

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

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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

とりあえずエラーを消す

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

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

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


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

C++

1QObject::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を割と意識してますが、こっちもうろ覚えだったりします。

C++

1/* JButton.hpp */ 2#pragma once 3#include <QtCore/QObject> 4 5class QPushButton; 6 7namespace sw_like { 8 class ActionListener; 9 10 class JButton : public QObject { 11 Q_OBJECT 12 13 private: 14 QPushButton *button_; 15 16 public: 17 JButton(void) = delete; 18 JButton(QString *label, QWidget *parent = Q_NULLPTR); 19 20 ~JButton(void) {} 21 22 void addActionListener(ActionListener *); 23 }; 24}

C++

1/* JButton.cpp */ 2#include "JButton.hpp" 3#include "ActionListener.hpp" 4 5#include <QtCore/QString> 6#include <QtWidgets/QPushButton> 7 8sw_like::JButton::JButton(QString *label, QWidget *parent) 9 : button_(new QPushButton(*label)) { 10 11 this->button_->setParent(parent); 12} 13 14void sw_like::JButton::addActionListener(sw_like::ActionListener *listener) { 15 QObject::connect(button_, SIGNAL(clicked()), 16 listener, SLOT(actionPerformed())); 17}

C++

1/* ActionListener.hpp */ 2#pragma once 3#include <QtCore/QObject> 4 5namespace sw_like { 6 class ActionListener : public QObject { 7 Q_OBJECT 8 9 public: 10 ActionListener(void) {}; 11 virtual ~ActionListener(void) {} 12 13 public slots: 14 virtual void actionPerformed(void) = 0; 15 }; 16}

C++

1/* header.hpp */ 2#pragma once 3#include "ActionListener.hpp" 4#include <QtCore/QObject> 5 6namespace ls { 7 class ButtonListener : public sw_like::ActionListener { 8 Q_OBJECT 9 10 public: 11 ButtonListener(void) {} 12 ~ButtonListener(void) {} 13 14 public slots: 15 void actionPerformed(void) override; 16 }; 17}

C++

1/* main.cpp */ 2#include "header.hpp" 3#include "ActionListener.hpp" 4#include "JButton.hpp" 5 6#include <QtCore/QDebug> 7#include <QtCore/QString> 8#include <QtWidgets/QApplication> 9#include <QtWidgets/QDialog> 10 11void ls::ButtonListener::actionPerformed(void) { 12 qDebug() << "Button"; 13} 14 15int main(int argc, char **argv) { 16 QApplication app(argc, argv); 17 QDialog *myDialog = new QDialog(); 18 19 auto *myButton = new sw_like::JButton(new QString("click"), myDialog); 20 auto *buttonListener = new ls::ButtonListener(); 21 22 myButton->addActionListener(buttonListener); 23 24 myDialog->show(); 25 return app.exec(); 26}

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

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

投稿2017/09/09 09:12

編集2017/09/09 10:11
LouiS0616

総合スコア35658

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

BeatStar

2017/09/09 09:36

ご回答ありがとうございます! なるほど。 クラス名::メンバ関数名 で明示するのですか。 今外出先なので、帰宅後試してみます!
LouiS0616

2017/09/09 09:39

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問