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

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

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

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

C++

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

Q&A

解決済

1回答

1217閲覧

QT5 スロットに対する疑問点

退会済みユーザー

退会済みユーザー

総合スコア0

Qt

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

C++

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

0グッド

1クリップ

投稿2020/05/25 16:54

編集2020/05/26 00:21

質問内容

C++もQTも共に初心者の者です。
下記コードではウィンドウ中にひとつだけボタンがあり
そのボタンを押すと標準出力にpushと表示させるプログラムです。

その中のwindow.uiはUICでコンパイルすると
ボタンクラスの親ウィジェットであるウィンドウクラス(Ui::Window1)
を含むui_window.h生成します。

私はwindow.uiを作成するときQT DesignerでUi::Window1にmy_slot()を追加し
ボタンクラスのclicked()シグナルとひも付けました。
なのでボタンが押されたらUi::Window1::my_slot()が実行されると認識しております。

しかしそのmy_slot()メソッドはUi::Window1のメソッドです。
mainwindow.cpp内のWinクラス(最高位のウィンドウ)の
文字列"push"を標準出力する処理が書かれたmy_slot()メソッドではありません。

またWinクラスが継承しているクラスはQMainWindowクラスであり
Ui::Window1はメンバとして所持しているだけなので
継承元のメソッドをオーバーライドしているようにも思えません。

なぜボタンが押された時紐付けられていないはずの
Win::my_slot()が呼び出されるのでしょうか。

どうかご回答よろしくお願いいたします。

実行時の画像

イメージ説明

コードやフォルダの構造

  • フォルダの構造

terminal

1. 2├── CMakeLists.txt <- CMakeスクリプト 3├── bin 4│   └── *a.out(実行ファイル本体)* 5├── build 6│   └── *ビルドやコンパイルで生成されるファイル郡* 7├── main.cpp <- main関数を含むクラス 8├── mainwindow.cpp <- Winクラスの実装 9├── mainwindow.hpp <- Winクラスのヘッダ 10└── window.ui <- ウィンドウやボタンの詳細を記したファイル 11 12
  • CMake用スクリプト CMakeLists.txt

Cmake

1################ 2# 変数定義 3################ 4 5#生成する実行ファイル名 6set ( OUT_PUT_FILE_NAME a.out) 7 8#インクルードディレクトリ 9set( INCLUDE_DIRS 10 /usr/include 11 ${CMAKE_SOURCE_DIR}/include 12) 13 14#リンクディレクトリ 15set( LINK_DIRS 16 /usr/lib/x86_64-linux-gnu 17 ${CMAKE_SOURCE_DIR}/lib 18) 19 20#リンクライブラリファイル名 21set( LINK_NAME 22 23) 24 25#このディレクトリの実装ファイル 26file( GLOB CPP_FILES 27 ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp 28 ${CMAKE_CURRENT_SOURCE_DIR}/*.ui 29 ${CMAKE_CURRENT_SOURCE_DIR}/*.qrc 30) 31 32#コンパイラ指定 33set(CMAKE_C_COMPILER ${clang-6.0} ) 34set(CMAKE_CXX_COMPILER ${clang++-6.0}) 35 36############ 37# 実行 38############ 39 40#cmakeのバージョン指定 41cmake_minimum_required(VERSION 3.8.2) 42 43#プロジェクト名と言語指定 44project(QT5_wxsample CXX) 45 46#c++17使用 47set(CMAKE_CXX_STANDARD, 17) 48 49# オプション指定する 50set(CMAKE_CXX_FLAGS "-Wall") 51 52#デバッグ版とリリース版の設定 53set(CMAKE_CXX_FLAGS_DEBUG "-g3 -O0 -pg") 54set(CMAKE_CXX_FLAGS_RELEASE "-O2 -s -DNDEBUG -march=native") 55set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-g3 -Og -pg") 56set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -s -DNDEBUG -march=native") 57 58##################### 59# QT5使用のための項目 60##################### 61set(CMAKE_INCLUDE_CURRENT_DIR ON) 62set(CMAKE_AUTOMOC ON) 63set(CMAKE_AUTOUIC ON) 64set(CMAKE_AUTORCC ON) 65find_package(Qt5 REQUIRED COMPONENTS Widgets) 66 67#インクルードディレクトリ指定 68include_directories(${INCLUDE_DIRS}) 69 70#リンクディレクトリ指定 71link_directories(${LINK_DIRS}) 72 73#実行ファイルの生成先 74set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) 75 76#実行ファイル作成 77add_executable(${OUT_PUT_FILE_NAME} ${CPP_FILES}) 78 79#ライブラリとのリンク 80target_link_libraries(${OUT_PUT_FILE_NAME} ${LINK_NAME}) 81target_link_libraries(${OUT_PUT_FILE_NAME} Qt5::Widgets)
  • main関数を含む main.cpp

c++

1#include "mainwindow.hpp" 2#include <QApplication> 3 4int main(int argc, char *argv[]) 5{ 6 QApplication a(argc, argv); 7 Win w; 8 w.show(); 9 10 return a.exec(); 11}
  • Winクラス(自作ウィンドウ)のヘッダーを記載したファイル mainwindow.hpp

c++

1#pragma once 2 3#include <QMainWindow> 4 5namespace Ui { 6 class Window1; 7} 8 9class Win : public QMainWindow{ 10 Q_OBJECT 11 12 public: 13 explicit Win(QWidget *parent = 0); 14 ~Win(); 15 16 private slots: 17 void my_slot(); 18 19 private: 20 Ui::Window1 *ui; 21}; 22
  • winクラスの実装を記載したファイル mainwindow.cpp

c++

1#include "mainwindow.hpp" 2#include "ui_window.h" 3#include <iostream> 4 5Win::Win(QWidget *parent) : 6 QMainWindow(parent), 7 ui(new Ui::Window1) 8{ 9 ui->setupUi(this); 10} 11 12Win::~Win() 13{ 14 delete ui; 15} 16 17void Win::my_slot()// <ーなぜWinクラスのmy_slot()が呼び出されるのかわからない。 18{ 19 std::cout << "push" << std::endl; 20}
  • uiファイル(ビルド時にui_window.hに変換される)ファイル window.ui

ui

1<?xml version="1.0" encoding="UTF-8"?> 2<ui version="4.0"> 3 <class>Window1</class> 4 <widget class="QMainWindow" name="Window1"> 5 <property name="geometry"> 6 <rect> 7 <x>0</x> 8 <y>0</y> 9 <width>482</width> 10 <height>323</height> 11 </rect> 12 </property> 13 <property name="windowTitle"> 14 <string>Hello, World!</string> 15 </property> 16 <widget class="QWidget" name="centralWidget"> 17 <layout class="QGridLayout" name="gridLayout"> 18 <item row="0" column="0"> 19 <widget class="QPushButton" name="pushButton"> 20 <property name="text"> 21 <string>PushButton</string> 22 </property> 23 </widget> 24 </item> 25 </layout> 26 </widget> 27 <widget class="QMenuBar" name="menuBar"> 28 <property name="geometry"> 29 <rect> 30 <x>0</x> 31 <y>0</y> 32 <width>482</width> 33 <height>27</height> 34 </rect> 35 </property> 36 <widget class="QMenu" name="menuFile"> 37 <property name="title"> 38 <string>&amp;File</string> 39 </property> 40 <addaction name="actionExit"/> 41 </widget> 42 <addaction name="menuFile"/> 43 </widget> 44 <widget class="QStatusBar" name="statusBar"/> 45 <action name="actionExit"> 46 <property name="text"> 47 <string>E&amp;xit</string> 48 </property> 49 </action> 50 </widget> 51 <layoutdefault spacing="6" margin="11"/> 52 <tabstops> 53 <tabstop>pushButton</tabstop> 54 </tabstops> 55 <resources/> 56 <connections> 57 <connection> 58 <sender>pushButton</sender> 59 <signal>clicked()</signal> 60 <receiver>Window1</receiver> 61 <slot>my_slot()</slot> 62 <hints> 63 <hint type="sourcelabel"> 64 <x>84</x> 65 <y>150</y> 66 </hint> 67 <hint type="destinationlabel"> 68 <x>93</x> 69 <y>55</y> 70 </hint> 71 </hints> 72 </connection> 73 </connections> 74 <slots> 75 <slot>my_slot()</slot> 76 </slots> 77</ui>

開発環境の備考

ツールの種類ツールの名前バージョン
コンパイラclang++6.0.0
コンパイルツールGNU Make4.1
コンパイルツールCMake3.17.2
GUIツールQT5.6.2
OSLinux Mint18.3

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

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

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

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

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

guest

回答1

0

ベストアンサー

私はwindow.uiを作成するときQT DesignerでUi::Window1にmy_slot()を追加し

デザイナーはあまり使ったことがないので、この部分は解りませんが

しかしそのmy_slot()メソッドはUi::Window1のメソッドです。

については、

xml

1 <class>Window1</class> 2 <widget class="QMainWindow" name="Window1"> 3 4 <-- 中略 --> 5 6 <connection> 7 <sender>pushButton</sender> 8 <signal>clicked()</signal> 9 <receiver>Window1</receiver> 10 <slot>my_slot()</slot>

注意点: "class" と "widget name" の双方が "Window1"

receiver がどちらの Window1 を指すのかというのが疑問点だと思いますが、
実際に生成されるコードを読むと、どの様になっているのか把握できると思います。

uic ファイルで実際に生成されるコードでは、以下のようになってます。(一部抜粋)

cpp

1// class class Ui_Window1 2 3 void setupUi(QMainWindow *Window1) 4 5 QObject::connect(pushButton, SIGNAL(clicked()), Window1, SLOT(my_slot()));

C++のコードでは、QMainWindow のインスタンスである Window1 に接続されてます。

my_slots() は QMainWindow のメソッドではありませんが、
これはダウンキャストした直接呼び出しではなく、Qt のシグナルストットの仕組みで呼び出されます。


uic window.ui で生成されるコード(全容)

cpp

1/******************************************************************************** 2** Form generated from reading UI file 'window.ui' 3** 4** Created by: Qt User Interface Compiler version 5.14.2 5** 6** WARNING! All changes made in this file will be lost when recompiling UI file! 7********************************************************************************/ 8 9#ifndef UI_WINDOW_H 10#define UI_WINDOW_H 11 12#include <QtCore/QVariant> 13#include <QtWidgets/QAction> 14#include <QtWidgets/QApplication> 15#include <QtWidgets/QGridLayout> 16#include <QtWidgets/QMainWindow> 17#include <QtWidgets/QMenu> 18#include <QtWidgets/QMenuBar> 19#include <QtWidgets/QPushButton> 20#include <QtWidgets/QStatusBar> 21#include <QtWidgets/QWidget> 22 23QT_BEGIN_NAMESPACE 24 25class Ui_Window1 26{ 27public: 28 QAction *actionExit; 29 QWidget *centralWidget; 30 QGridLayout *gridLayout; 31 QPushButton *pushButton; 32 QMenuBar *menuBar; 33 QMenu *menuFile; 34 QStatusBar *statusBar; 35 36 void setupUi(QMainWindow *Window1) 37 { 38 if (Window1->objectName().isEmpty()) 39 Window1->setObjectName(QString::fromUtf8("Window1")); 40 Window1->resize(482, 323); 41 actionExit = new QAction(Window1); 42 actionExit->setObjectName(QString::fromUtf8("actionExit")); 43 centralWidget = new QWidget(Window1); 44 centralWidget->setObjectName(QString::fromUtf8("centralWidget")); 45 gridLayout = new QGridLayout(centralWidget); 46 gridLayout->setSpacing(6); 47 gridLayout->setContentsMargins(11, 11, 11, 11); 48 gridLayout->setObjectName(QString::fromUtf8("gridLayout")); 49 pushButton = new QPushButton(centralWidget); 50 pushButton->setObjectName(QString::fromUtf8("pushButton")); 51 52 gridLayout->addWidget(pushButton, 0, 0, 1, 1); 53 54 Window1->setCentralWidget(centralWidget); 55 menuBar = new QMenuBar(Window1); 56 menuBar->setObjectName(QString::fromUtf8("menuBar")); 57 menuBar->setGeometry(QRect(0, 0, 482, 27)); 58 menuFile = new QMenu(menuBar); 59 menuFile->setObjectName(QString::fromUtf8("menuFile")); 60 Window1->setMenuBar(menuBar); 61 statusBar = new QStatusBar(Window1); 62 statusBar->setObjectName(QString::fromUtf8("statusBar")); 63 Window1->setStatusBar(statusBar); 64 65 menuBar->addAction(menuFile->menuAction()); 66 menuFile->addAction(actionExit); 67 68 retranslateUi(Window1); 69 QObject::connect(pushButton, SIGNAL(clicked()), Window1, SLOT(my_slot())); 70 71 QMetaObject::connectSlotsByName(Window1); 72 } // setupUi 73 74 void retranslateUi(QMainWindow *Window1) 75 { 76 Window1->setWindowTitle(QCoreApplication::translate("Window1", "Hello, World!", nullptr)); 77 actionExit->setText(QCoreApplication::translate("Window1", "E&xit", nullptr)); 78 pushButton->setText(QCoreApplication::translate("Window1", "PushButton", nullptr)); 79 menuFile->setTitle(QCoreApplication::translate("Window1", "&File", nullptr)); 80 } // retranslateUi 81 82}; 83 84namespace Ui { 85 class Window1: public Ui_Window1 {}; 86} // namespace Ui 87 88QT_END_NAMESPACE 89 90#endif // UI_WINDOW_H 91

投稿2020/05/26 06:33

teamikl

総合スコア8760

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

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

退会済みユーザー

退会済みユーザー

2020/05/26 07:27

teamiki様 丁寧なご回答誠にありがとうございます。 しかし >Qt のシグナルスロットの仕組みで呼び出されます。 の部分が私の知識ではわかりません。 勝手な憶測で申し訳ないのですがひょっとかして QTには親子ともに同じQObject(今回はQMainWindow)を継承しているのなら 子ウィンドウのスロットを親ウィンドウのスロットとして 使用できるような仕組みでもあるのでしょうか。 めちゃくちゃなこと言ってたらすいません... どうかお返事お待ちしております。
teamikl

2020/05/26 09:16 編集

まず、2つの "Window1" について混同されてるので そちらは今回の質問の内容とは異なります。 >ボタンが押されたらUi::Window1::my_slot()が実行されると認識しております。 はコードを見て貰うと解る通り Ui::Window1::my_slot() ではなく、Window1 my_slot() に接続されてます Ui::Window1 は my_slot() を持ってません。 Ui::Window1 は親ウインドウではなく、UI を生成するクラスです。<-- 恐らく誤解されてる箇所 そして、親ウィンドウ、小ウィンドウではなく、<-- 違った意味合いになります この場合は、親クラス(QMainWindow) と子クラス(Win) になります。 Ui::Window1 と QMainWindow は親子関係はありません。 QMainWindow *Window1 <- 変数の名前が "Window1" と一致するのみ ==== スロット呼び出しについて QMainWindow *Window1 の Window1::my_slot は、 ダウンキャストになるため安全ではありませんが、それとは異なり、 SIGNAL(), SLOT() マクロを用いた場合は、実行時に対象の名前のスロットが探されます。 >QObject ~仕組みでも有るのでしょうか 概ね予想されてるとおりですが、一部 >子ウィンドウのスロットを親ウィンドウのスロットとして については否。意図は解りますが、説明として(用語を含め)正しくない表現です。 QObject 共通の仕組みはあります。 QMetaObject という、実行時にクラスの持つ情報を調べる仕組みがあり、 C++のクラス機構にQt独自の拡張を施してる感じ。あまり馴染みがないかもしれませんが、 他の言語でも、例えばC#/Java等もリフレクションと呼ばれる同様の仕組みを備えてます。
退会済みユーザー

退会済みユーザー

2020/05/26 17:23 編集

返信に時間がかかってしまい申し訳ございません。 貴方様のご回答を見ながら clicked()とWin::my_slotのひも付けまでの過程と誤解してそうな点を 考えてみました。 [UICコンパイル時] ・Ui::Window1作成:UI を生成するクラスなので メンバにボタンやメニューバーなどを所持 そのメソッドsetupUiの引数名がWindow1なので QObject::connect(pushButton, SIGNAL(clicked()), Window1, SLOT(my_slot())); の引数Window1はUi::Window1ではない。 [Winクラス使用時] ・Winクラス内にスロットmy_slot()が定義されている。 my_slot()はUi::Window1::setupUi(QMainWindow *Window1) 内でQObject::connect()メソッドにより紐つけられている。 そこでSLOT(my_slot())をWindow1->my_slot() と書かなくても良い理由はmocでのコンパイル時に 自動的に引数に入力されたオブジェクトにmy_slot()があるかどうか検出してくれるから (テンプレートやストラテジーパターンのようなもの?) <- ここただの憶測です。 ・コンストラクタでui->setupUi(this);実行 ここでようやくWinクラスのmy_slot()とボタンのシグナルが紐つけられる。 上記のような解釈でよろしいでしょうか。 めちゃくちゃなこと言ってたらすいません... どうか御返事お待ちしております。
teamikl

2020/05/27 05:56

[UICコンパイル時] >引数Window1はUi::Window1ではない。 Yes. QMainWindow* Window1 とあるように変数名です。 元質問の回答としては、ここだけ理解されれば十分だと思います。 [Winクラス使用時] > そこでSLOT(my_slot())をWindow1->my_slot() と書かなくても良い理由 歴史的な理由によるもので (https://doc.qt.io/qt-5/why-moc.html) 昔は、コンパイラが対応してなくて SIGNAL()/SLOT() マクロでしか書けませんでした。 今は、シグナル・スロットの書き方は複数あり、関数ポインタや無名関数に直接コードを書いて呼び出す方法もあります。 (デザイナーを使う場合は、あまり使う機会はないかもしれません) > mocでのコンパイル時に自動的に引数に入力されたオブジェクトにmy_slot()があるかどうか検出してくれるから ここの実装内部の理解は少し怪しいです。 まず、シグナル・スロットの接続には複数手段があることを理解した上で、 SIGNAL()/SLOT() を用いる場合、シグナル・スロット・プロパティ等のメタオブジェクトが提供する要素は、 実行時に変更可能なため、mocコンパイル時やコンパイル時に決定することは出来ません。 ui のコンパイルの時にスロットが検査されるかどうかについては、 自分は詳しくありませんが、C++ のコード上では コンパイルの時点では SLOT() の中身は検査されません。 「QObject::connect の実行時に」、スロットの引数がシグナルと一致しない場合、接続が失敗します。 関数ポインタでの接続なら、コンパイル時にスロットの存在をチェックできます。 ----- > (テンプレートやストラテジーパターンのようなもの?) <- ここただの憶測です。 ここは漠然としすぎていて、返答しかねます。 ---- > ・コンストラクタでui->setupUi(this);実行 > ここでようやくWinクラスのmy_slot()とボタンのシグナルが紐つけられる。 Yes 実際に紐付けているのは setupUi 内の QObject::connect
退会済みユーザー

退会済みユーザー

2020/05/27 17:43

teamikl様 とてもわかりやすく 教えていただき誠にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問