「Qtの基礎 - ウインドウ」の版間の差分
|  (→使用例 4) | 細 (文字列「__FORCETOC__」を「{{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This pag…) | ||
| (同じ利用者による、間の12版が非表示) | |||
| 1行目: | 1行目: | ||
| == 概要 == | == 概要 == | ||
| Qtにおいて、ウィンドウのメニューバー、ツールバー、ステータスバーの作成手順を記載する。<br> | Qtにおいて、ウィンドウのメニューバー、ツールバー、ステータスバーの作成手順を記載する。<br> | ||
| <br><br> | |||
| == タイトルバー == | |||
| ==== アイコン ==== | |||
| ウインドウのタイトルバーにアイコンを指定するには、<code>setWindowIcon</code>メソッドを使用する。<br> | |||
| <center> | |||
| {| class="wikitable" style="background-color:#fefefe;" | |||
| |- | |||
| ! style="background-color:#66CCFF;" | データ  | |||
| ! style="background-color:#66CCFF;" | アイコンの設定例(QIconクラス) | |||
| |- | |||
| | style="text-align: center;" | 標準画像 ||  | |||
| <syntaxhighlight lang="c++">QIcon Icon = style()->standardIcon(QStyle::SP_MediaPlay); | |||
| QApplication::setWindowIcon(Icon);</syntaxhighlight> | |||
| |- | |||
| | style="text-align: center;" | 標準カーソル || | |||
| <syntaxhighlight lang="c++">QCursor Cursor = Qt::WaitCursor; | |||
| if(!Cursor.pixmap().isNull()) | |||
| { | |||
|   QIcon Icon = QIcon(QPixmap(csr.pixmap())); | |||
|   QApplication::setWindowIcon(Icon); | |||
| } | |||
| </syntaxhighlight> | |||
| |- | |||
| | style="text-align: center;" | リソースの指定 ||  | |||
| <syntaxhighlight lang="c++"> | |||
| QIcon Icon = QIcon(":/images/mouse.png"); | |||
| QApplication::setWindowIcon(Icon); | |||
| </syntaxhighlight> | |||
| |- | |||
| | style="text-align: center;" | ファイルパスの指定 ||  | |||
| <syntaxhighlight lang="c++"> | |||
| QIcon Icon = QIcon("C:/temp/testimage.ico"); | |||
| QApplication::setWindowIcon(Icon); | |||
| </syntaxhighlight> | |||
| |} | |||
| </center> | |||
| <br> | |||
| ==== タイトルバーの非表示 ==== | |||
| <code>setWindowFlags</code>メソッドを使用して、<code>Qt::FramelessWindowHint</code>フラグを設定することにより、ウインドウのタイトルバーが非表示になる。<br> | |||
| この<code>Qt::FramelessWindowHint</code>フラグを使用すると、最大化/最小化ボタン、ウインドウの境界線、タイトルバーが非表示になる。<br> | |||
| <br> | |||
| したがって、このフラグを使用することにより、ウインドウ全体がカスタム外観になり、デフォルトのウインドウ機能が失われる。<br> | |||
| また、必要に応じて、これらの機能を再実装することもできる。<br> | |||
|  <syntaxhighlight lang="c++"> | |||
|  #include <QGUIApplication> | |||
|  #include <QMainwindow> | |||
|  int main(int argc, char *argv[]) | |||
|  { | |||
|     QGUIApplication a(argc, argv); | |||
|     QMainWindow Window; | |||
|     // タイトルバーを非表示にする | |||
|     Window.setWindowFlags(Qt::FramelessWindowHint); | |||
|     Window.show(); | |||
|     return a.exec(); | |||
|  } | |||
|  </syntaxhighlight> | |||
| <br><br> | <br><br> | ||
| 63行目: | 125行目: | ||
| メニューを選択してリソースファイルを上書き保存する。<br> | メニューを選択してリソースファイルを上書き保存する。<br> | ||
| [ファイル] - [<リソースファイル名>の保存]<br> | [ファイル] - [<リソースファイル名>の保存]<br> | ||
| <br> | |||
| ==== ツールボタンの画像 ==== | |||
| ツールバーのツールボタンでは、アイコンとテキストが表示できる。<br> | |||
| <br> | |||
| ツールボタンは常に固定サイズであり、アイコンの位置はテキストの左または下を指定する。<br> | |||
| また、アイコンのサイズも指定できる。<br> | |||
|  <syntaxhighlight lang="c++"> | |||
|  std::unique_ptr<QToolButton> pToolBtn = std::make_unique<QToolButton>(); | |||
|  QIcon Icon = QIcon(style()->standardPixmap(QStyle::SP_TitleBarMenuButton)); | |||
|  pToolBtn->setIcon(Icon); | |||
|  pToolBtn->setText(tr("Test")); | |||
|  pToolBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); | |||
|  </syntaxhighlight> | |||
| <br> | |||
| アイコンのサイズを指定しない場合、原画像のサイズで表示する。<br> | |||
| プッシュボタンとは異なり、スタイルを指定しない場合はアイコンのみを表示する。<br> | |||
| <br> | |||
| 下表に、ボタンスタイルの定義を示す。<br> | |||
| <br> | |||
| <u>※注意</u><br> | |||
| <u>ツールボタンは、レイアウトの伸縮に影響されずに同じサイズを保持する。</u><br> | |||
| <center> | |||
| 表. ボタンスタイルの定義(enum Qt::ToolButtonStyle)<br> | |||
| {| class="wikitable" style="background-color:#fefefe;" | |||
| |- | |||
| ! style="background-color:#00ffff;" | 定義  | |||
| ! style="background-color:#00ffff;" | 値  | |||
| ! style="background-color:#00ffff;" | 説明 | |||
| |- | |||
| | ToolButtonIconOnly || 0 || アイコンのみ | |||
| |- | |||
| | ToolButtonTextOnly || 1 || テキストのみ | |||
| |- | |||
| | ToolButtonTextBesideIcon || 2 || アイコンの横にテキスト | |||
| |- | |||
| | ToolButtonTextUnderIcon || 3 || アイコンの下にテキスト | |||
| |- | |||
| | ToolButtonFollowStyle || 4 || スタイルに従う | |||
| |} | |||
| </center> | |||
| <br><br> | <br><br> | ||
| 154行目: | 257行目: | ||
|   </syntaxhighlight> |   </syntaxhighlight> | ||
| <br><br> | <br><br> | ||
| == タブオーダーの設定 == | |||
| タブオーダーを無効化するには、ウインドウにある全てのウィジェットのタブオーダーを自分自身にする。<br> | |||
|  <syntaxhighlight lang="c++"> | |||
|  QList<QWidget *>widgets = findChildren<QWidget *>(); | |||
|  foreach(QWidget *widget, widgets) | |||
|  { | |||
|     widget->setTabOrder(widget, widget); | |||
|  } | |||
|  </syntaxhighlight> | |||
| <br><br> | |||
| == イベント == | |||
| ==== リサイズイベント ==== | |||
| <code>resizeEvent</code>メソッドは、ウィジェットのサイズが変化した時に発生する。<br> | |||
|  <syntaxhighlight lang="c++"> | |||
|  // Mainwindow.h | |||
|  // ... 略 | |||
|  protected: | |||
|     void resizeEvent(QResizeEvent *event); | |||
|  </syntaxhighlight> | |||
| <br> | |||
|  <syntaxhighlight lang="c++"> | |||
|  // Mainwindow.cpp | |||
|  // ウインドウの幅と高さをデバッグ出力する | |||
|  void MainWindow::resizeEvent(QResizeEvent *event) | |||
|  { | |||
|     qDebug("Resize old=%dx%d,new=%dx%d", event->oldSize().width(), event->oldSize().height(), event->size().width(), event->size().height() ); | |||
|  } | |||
|  </syntaxhighlight> | |||
| <br> | |||
| ==== クローズイベント ==== | |||
| <code>closeEvent</code>メソッドは、タイトルバーの×ボタンを押下した時やcloseメソッドを実行する時に発生する。<br> | |||
|  <syntaxhighlight lang="c++"> | |||
|  // Mainwindow.h | |||
|  // ...略 | |||
|  protected: | |||
|     void closeEvent(QCloseEvent *event); | |||
|  private: | |||
|     bool checkQuit; | |||
|  </syntaxhighlight> | |||
| <br> | |||
| コンストラクタでスイッチ(変数checkQuit)を初期化する。<br> | |||
| スイッチをtrueにする時、ウインドウを閉じる直前に確認メッセージのYes / Noメッセージボックスを表示する。<br> | |||
| Noを選択する場合、終了処理を行わずに元の画面に戻る。<br> | |||
| Yesを選択する場合、ウインドウを閉じて終了する。<br> | |||
|  <syntaxhighlight lang="c++"> | |||
|  // Mainwindow.cpp | |||
|  MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) | |||
|  { | |||
|     checkQuit = true; | |||
|  } | |||
|  </syntaxhighlight> | |||
| <br> | |||
|  <syntaxhighlight lang="c++"> | |||
|  // Mainwindow.cpp  | |||
|  void MainWindow::closeEvent(QCloseEvent *event) | |||
|  { | |||
|     if(checkQuit) | |||
|     { | |||
|        int ret = QMessageBox::question(this, tr("終了確認"), tr("終了しますか?")); | |||
|        if(ret == QMessageBox::No) | |||
|        { | |||
|           event->ignore(); | |||
|        } | |||
|     } | |||
|     event->accept(); | |||
|  } | |||
|  </syntaxhighlight> | |||
| <br><br> | |||
| == ウインドウおよびウィジェットの形状の変更 == | |||
| ウインドウおよびプッシュボタン等のウィジェットにおいて、形状を非矩形に変更する、および、背景を画像に変更する手順を記載する。<br> | |||
| <br> | |||
| ==== 非矩形ウインドウ ==== | |||
| まず、型抜き用画像を作成する。<br> | |||
| 型抜き用画像は、ウインドウの形状を表す部分を描画した画像のことであり、画像以外の箇所を透過ピクセルにする。<br> | |||
| したがって、型抜き用画像は透過(アルファチャンネル)をサポートするフォーマットで保存する必要がある。<br> | |||
| <br> | |||
| これは、[https://inkscape.org/ja/ Inkscape](ソフトウェア)を使用して、ウィンドウの形状を描画してPNG形式で保存する方法を推奨する。<br> | |||
| <br> | |||
| 次に、QMainWindowクラスまたはQDialogクラスのコンストラクタにおいて、以下のように記述する。<br> | |||
|  <syntaxhighlight lang="c++"> | |||
|  MainWindow::MainWindow() | |||
|  { | |||
|     Qt::WindowFlags flags = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::FramelessWindowHint; | |||
|     setWindowFlags(flags); | |||
|     QPixmap pixmap("window.png"); | |||
|     setMask(pixmap.mask()); | |||
|     // 型抜き画像のサイズに合わせる | |||
|     resize( 300, 300 ); | |||
|  } | |||
|  </syntaxhighlight> | |||
| <br> | |||
| ==== ウインドウおよびウィジェットの外観の変更 ==== | |||
| 例えば、ウインドウの背景色を変更する場合、QMainWindowクラスまたはQDialogクラスのコンストラクタにおいて、以下のように記述する。 | |||
|  <syntaxhighlight lang="c++"> | |||
|  MainWindow::MainWindow() | |||
|  { | |||
|     // 背景色を設定する | |||
|     setStyleSheet("* {background-color : rgb( 255, 255, 128 )}"); | |||
|     // 透過率を設定する | |||
|     setWindowOpacity(0.7); | |||
|  } | |||
|  </syntaxhighlight> | |||
| <br> | |||
| また、上記の変更は、Qt Designerから実行することもできる。<br> | |||
| Qt Designer画面から、ウインドウまたはウィジェットの[プロパティ] - [styleSheet]項目を選択する。<br> | |||
| [スタイルシートを変更]画面が開くので、[〜を追加]プルダウンから"background-color"と"Opacity"のQSSを追加する。<br> | |||
| <br> | |||
| ==== QSSをプロジェクト全体に適用する ==== | |||
| 一般的に、QSSの設定は各ウィジェットごとに設定するが、プロジェクト全体で共通のQSSを設定することもできる。<br> | |||
| QSSの書式や構文は、CSSとほぼ同じである。<br> | |||
| <br> | |||
| 以下の例は、Qtに付属しているサンプルを参考にしている。<br> | |||
| (<Qtのインストールディレクトリ>/Examples/Qt-<バージョン名>/widgets/widgets/stylesheetディレクトリ)<br> | |||
| まず、MainWindow.qssファイルを作成する。<br> | |||
|  <syntaxhighlight lang="css"> | |||
|  // MainWindow.qss | |||
|  // QPushButtonの設定例 | |||
|  QPushButton | |||
|  { | |||
|     background-color: #000088; | |||
|     border-color: #000044; | |||
|     border-style: solid; | |||
|     border-radius: 5; | |||
|  } | |||
|  QPushButton:hover | |||
|  { | |||
|    background-color: #880000; | |||
|  } | |||
|  QPushButton:pressed | |||
|  { | |||
|     background-color: #008800; | |||
|  } | |||
|  </syntaxhighlight> | |||
| <br> | |||
| 次に、MainWindow.qssファイルをQApplicationクラスのsetStyleSheetメソッドに渡す。<br> | |||
| QApplicationクラスのインスタンスはシングルトンとして生成されているため、そのインスタンスから実行する。<br> | |||
| また、QSSファイルはリソースとして登録しておくとよい。<br> | |||
| <br> | |||
| 以下の例では、main関数でQSSファイルを読み込み、設定している。<br> | |||
| QSSには、他にも様々なクラスやセレクタが存在する。<br> | |||
|  <syntaxhighlight lang="c++"> | |||
|  #include "MainWindow.h" | |||
|  #include <QtCore> | |||
|  int main(int argc, char *argv[]) | |||
|  { | |||
|     QApplication app(argc, argv); | |||
|     QFile file(":/qss/MainWindow.qss"); | |||
|     file.open( QFile::ReadOnly ); | |||
|     QString strStyles = QLatin1String(file.readAll()); | |||
|     app.setStyleSheet( strStyles ); | |||
|     MainWindow w; | |||
|     w.show(); | |||
|     return app.exec(); | |||
|  } | |||
|  </syntaxhighlight> | |||
| <br> | |||
| ==== ウィンドウの背景を画像にする ==== | |||
| <code>QStyle</code>クラスは、ウィンドウの外見を詳細に設定することができる。<br> | |||
| <br> | |||
| <code>QStyle</code>クラスを継承した派生クラスを作成して、必要な項目のうち、比較的簡単に設定できる項目である。<br> | |||
| <code>QStyle</code>クラスを継承した派生クラスは数種類用意されており、OSにより使用できるものが決まっている。<br> | |||
| <br> | |||
| 以下の例は、<code>QStyle</code>クラスを継承した派生クラスを作成している。<br> | |||
| <br> | |||
| 設定後、<code>QMainWindow</code>クラスまたは<code>QDialog</code>クラスのコンストラクタにおいて、以下のように記述して適用する。<br> | |||
|  <syntaxhighlight lang="c++"> | |||
|  QApplication::setStyle(new MyStyle);  // MyStyleクラスは、QStyleクラスの派生クラス | |||
|  </syntaxhighlight> | |||
| <br> | |||
| ウィンドウの背景を画像にするには、まず、<code>polish</code>メソッドをオーバーライドする。<br> | |||
| 次に、画像を設定するためのsetTextureメソッド(メソッド名は任意)を作成する。<br> | |||
|  <syntaxhighlight lang="c++"> | |||
|  #pragma once | |||
|  #include <QStyle> | |||
|  class MyStyle : public QStyle | |||
|  { | |||
|  public: | |||
|     MyStyle(); | |||
|     ~MyStyle(); | |||
|     void polish(QPalette &palette); | |||
|  private: | |||
|     static void setTexture(QPalette &palette, QPalette::ColorRole role, const QPixmap &pixmap); | |||
|  }; | |||
|  </syntaxhighlight> | |||
| <br> | |||
| <code>polish</code>メソッドとsetTextureメソッドは、以下のように記述する。<br> | |||
| 背景画像は、型抜き画像としても使用することができる。したがって、背景用画像と型抜き用画像の2つを用意する必要はない。<br> | |||
|  <syntaxhighlight lang="c++"> | |||
|  void MyStyle::polish(QPalette &palette) | |||
|  { | |||
|     QPixmap backgroundImage("window.png"); | |||
|     setTexture(palette, QPalette::Window, backgroundImage); | |||
|  } | |||
|  void MyStyle::setTexture(QPalette &palette, QPalette::ColorRole role, const QPixmap &pixmap) | |||
|  { | |||
|     for(int i = 0; i < QPalette::NColorGroups; ++i) | |||
|     { | |||
|        QColor color = palette.brush(QPalette::ColorGroup(i), role).color(); | |||
|        palette.setBrush(QPalette::ColorGroup(i), role, QBrush(color, pixmap)); | |||
|     } | |||
|  } | |||
|  </syntaxhighlight> | |||
| <br> | |||
| ==== ボタンのスタイルの変更 ==== | |||
| 上記のセクションにも記述した<code>polish</code>メソッドを使用して、ボタンのスタイルを変更することができる。<br> | |||
| <br> | |||
| <code>QPalette</code>クラスのコンストラクタでは、ボタンの色を定義する。<br> | |||
| ウィンドウおよびコントロールの設定の詳細は、[http://doc-snapshot.qt-project.org/4.8/qpalette.html Qtの公式WebサイトのQPalleteリファレンス]を参照すること。<br> | |||
|  <syntaxhighlight lang="c++"> | |||
|  void MyStyle::polish(QPalette &palette) | |||
|  { | |||
|     QPixmap backgroundImage("window.png"); | |||
|     // 追加部分 : ボタンスタイルを変更 | |||
|     palette = QPalette(QColor(0, 0, 0, 50)); | |||
|     palette.setBrush(QPalette::ButtonText, Qt::cyan); | |||
|     setTexture(palette, QPalette::Window, backgroundImage); | |||
|  } | |||
|  </syntaxhighlight> | |||
| <br> | |||
| また、ボタンの背景を画像に変更するには、以下のように記述する。<br> | |||
|  <syntaxhighlight lang="c++"> | |||
|  void MyStyle::polish(QPalette &palette) | |||
|  { | |||
|     QPixmap backgroundImage("window.png"); | |||
|     // 追加部分 : ボタンの背景を画像にする場合 | |||
|     QPixmap buttonImage("button.png"); | |||
|     // 追加部分 : ボタンを押下した時の背景 | |||
|     QPixmap midImage = buttonImage; | |||
|     setTexture(palette, QPalette::Button, buttonImage); | |||
|     setTexture(palette, QPalette::Mid, midImage); | |||
|     setTexture(palette, QPalette::Window, backgroundImage); | |||
|  } | |||
|  </syntaxhighlight> | |||
| <br> | |||
| ==== ウインドウおよびウィジェットの形状を変更する ==== | |||
| ウインドウおよびウィジェットの形状を変更するには、ウィジェットの形状をプログラムで全て記述する必要がある。<br> | |||
| <br> | |||
| 例えば、ボタンの形状を変更する場合、外形を描画するだけではなく、<br> | |||
| ボタンが立体的に見えるように陰影の描画、ボタン押下時の陰影の変化等も記述する必要がある。<br> | |||
| (或いは、QSS + 背景画像で形状を変更できる可能性がある)<br> | |||
| <br> | |||
| ウインドウおよびウィジェットの形状を記述するには、<code>QStyle</code>クラスを継承した派生クラスを作成して、<br> | |||
| <code>QStyle</code>クラスの<code>drawPrimitive</code>メソッドをオーバーライドする。<br> | |||
| <br> | |||
| 以下の例では、ボタンの形状を変更している。<br> | |||
| Qtに付属しているサンプルを参考にしている。<br> | |||
| (<Qtのインストールディレクトリ>/Examples/Qt-<バージョン名>/widgets/widgets/styles/norwegianwoodstyle.cppファイル)<br> | |||
|  <syntaxhighlight lang="c++"> | |||
|  void MyStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const | |||
|  { | |||
|     switch(element) | |||
|     { | |||
|        // ウィジェットのPrimitiveの種類を指定する(ウィジェットの種類ではない) | |||
|        case PE_PanelButtonCommand: | |||
|           int delta = (option->state & State_MouseOver) ? 64 : 0; | |||
|           QColor slightlyOpaqueBlack(0, 0, 0, 63); | |||
|           QColor semiTransparentWhite(255, 255, 255, 127 + delta); | |||
|           QColor semiTransparentBlack(0, 0, 0, 127 - delta); | |||
|           int x, y, width, height; | |||
|           option->rect.getRect(&x, &y, &width, &height); | |||
|           // 以下の3行のみ追記する | |||
|           // ボタンの形状を楕円にする | |||
|           QPainterPath path; | |||
|           path.addEllipse(option->rect); | |||
|           // これ以降の記述はコピーである | |||
|           // ボタンの押下時と非押下時の陰影等を記述する | |||
|           // 様々な形状に対応している | |||
|           int radius = qMin(width, height) / 2; | |||
|           QBrush brush; | |||
|           bool darker; | |||
|           const QStyleOptionButton *buttonOption = qstyleoption_cast<const QStyleOptionButton *>(option); | |||
|           if(buttonOption && (buttonOption->features & QStyleOptionButton::Flat)) | |||
|           { | |||
|              brush = option->palette.background(); | |||
|              darker = (option->state & (State_Sunken | State_On)); | |||
|           } | |||
|           else | |||
|           { | |||
|              if(option->state & (State_Sunken | State_On)) | |||
|              { | |||
|                 brush = option->palette.mid(); | |||
|                 darker = !(option->state & State_Sunken); | |||
|              } | |||
|              else | |||
|              { | |||
|                 brush = option->palette.button(); | |||
|                 darker = false; | |||
|              } | |||
|           } | |||
|           painter->save(); | |||
|           painter->setRenderHint(QPainter::Antialiasing, true); | |||
|           painter->fillPath(path, brush); | |||
|           if(darker) | |||
|           { | |||
|              painter->fillPath(path, slightlyOpaqueBlack); | |||
|           } | |||
|           int penWidth; | |||
|           if(radius < 10) | |||
|              penWidth = 3; | |||
|           else if (radius < 20) | |||
|              penWidth = 5; | |||
|           else | |||
|              penWidth = 7; | |||
|           QPen topPen(semiTransparentWhite, penWidth); | |||
|           QPen bottomPen(semiTransparentBlack, penWidth); | |||
|           if(option->state & (State_Sunken | State_On)) | |||
|           { | |||
|              qSwap(topPen, bottomPen); | |||
|           } | |||
|           int x1 = x; | |||
|           int x2 = x + radius; | |||
|           int x3 = x + width - radius; | |||
|           int x4 = x + width; | |||
|           if(option->direction == Qt::RightToLeft) | |||
|           { | |||
|              qSwap(x1, x4); | |||
|              qSwap(x2, x3); | |||
|           } | |||
|           QPolygon topHalf; | |||
|           topHalf << QPoint(x1, y) << QPoint(x4, y) << QPoint(x3, y + radius) << QPoint(x2, y + height - radius) << QPoint(x1, y + height); | |||
|           painter->setClipPath(path); | |||
|           painter->setClipRegion(topHalf, Qt::IntersectClip); | |||
|           painter->setPen(topPen); | |||
|           painter->drawPath(path); | |||
|           QPolygon bottomHalf = topHalf; | |||
|           bottomHalf[0] = QPoint(x4, y + height); | |||
|           painter->setClipPath(path); | |||
|           painter->setClipRegion(bottomHalf, Qt::IntersectClip); | |||
|           painter->setPen(bottomPen); | |||
|           painter->drawPath(path); | |||
|           painter->setPen(option->palette.foreground().color()); | |||
|           painter->setClipping(false); | |||
|           painter->drawPath(path); | |||
|           painter->restore(); | |||
|           break; | |||
|        default: | |||
|           // ボタン以外はMotifStyleの形状を採用 | |||
|           QMotifStyle::drawPrimitive(element, option, painter, widget); | |||
|           break; | |||
|     } | |||
|  } | |||
|  </syntaxhighlight> | |||
| <br><br> | |||
| {{#seo: | |||
| |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki | |||
| |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板 | |||
| |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux | |||
| |image=/resources/assets/MochiuLogo_Single_Blue.png | |||
| }} | |||
| __FORCETOC__ | __FORCETOC__ | ||
| [[カテゴリ:Qt]] | [[カテゴリ:Qt]] | ||
2024年10月14日 (月) 10:58時点における最新版
概要
Qtにおいて、ウィンドウのメニューバー、ツールバー、ステータスバーの作成手順を記載する。
タイトルバー
アイコン
ウインドウのタイトルバーにアイコンを指定するには、setWindowIconメソッドを使用する。
| データ | アイコンの設定例(QIconクラス) | 
|---|---|
| 標準画像 | QIcon Icon = style()->standardIcon(QStyle::SP_MediaPlay);
QApplication::setWindowIcon(Icon);
 | 
| 標準カーソル | QCursor Cursor = Qt::WaitCursor;
if(!Cursor.pixmap().isNull())
{
  QIcon Icon = QIcon(QPixmap(csr.pixmap()));
  QApplication::setWindowIcon(Icon);
}
 | 
| リソースの指定 | QIcon Icon = QIcon(":/images/mouse.png");
QApplication::setWindowIcon(Icon);
 | 
| ファイルパスの指定 | QIcon Icon = QIcon("C:/temp/testimage.ico");
QApplication::setWindowIcon(Icon);
 | 
タイトルバーの非表示
setWindowFlagsメソッドを使用して、Qt::FramelessWindowHintフラグを設定することにより、ウインドウのタイトルバーが非表示になる。
このQt::FramelessWindowHintフラグを使用すると、最大化/最小化ボタン、ウインドウの境界線、タイトルバーが非表示になる。
したがって、このフラグを使用することにより、ウインドウ全体がカスタム外観になり、デフォルトのウインドウ機能が失われる。
また、必要に応じて、これらの機能を再実装することもできる。
 #include <QGUIApplication>
 #include <QMainwindow>
 
 int main(int argc, char *argv[])
 {
    QGUIApplication a(argc, argv);
 
    QMainWindow Window;
 
    // タイトルバーを非表示にする
    Window.setWindowFlags(Qt::FramelessWindowHint);
 
    Window.show();
 
    return a.exec();
 }
メニュー
サブメニュー名の&の直後の文字列がニーモニックとして使用される。
下図の"File"はQMenuクラス、"Open"はQActionクラスである。
"Open"の右側にあるアイコンを選択するとサブメニューを入力できる。
サブメニューを入力すると、その親メニュー(上図では、"Open")はQMenuクラスに変更される。
追加したアクションは、Qt Designer画面下にある[アクションエディタ]ペインに表示される。
ツールバー
ここでは、ツールバーのアイコン(24x24のpng形式が標準)を作成したとする。
リソースファイルの作成
[ファイル] - [ファイル/プロジェクトの新規作成] - 画面左の[ファイルとクラス] - [Qt] - [Qt リソースファイル]を選択する。
Qtのリソースディレクトリ名をResource、ディレクトリパスはプロジェクト直下とする。
リソースエディタを開く
Qtのメイン画面左にあるプロジェクトツリーの[Resources]直下に、リソースファイル(.qrc)が表示されるので、
リソースファイルを右クリックして、[エディタで開く]を選択する。
※注意
リソースファイルの新規作成時は、自動的にリソースエディタが開く。
新規登録
以下に、リソースファイルが空の場合の登録手順を示す。
- まず、リソースエディタを開いて、[Add Prefix]ボタンを押下する。
 または、プロジェクトツリーにあるリソースファイルを右クリックして、[プレフィックスの追加...]を選択する。
- プレフィックス名に/(スラッシュ)を入力する。
- 次に、[Add Files]ボタンを押下する。
- ファイル選択ダイアログ画面にて、リソースファイルを選択する。([Ctrl] + [A]キーで全選択できる)
※注意
プロジェクトツリーにあるリソースファイルを右クリックして、[既存ファイルの追加...]を選択する場合、既定のプレフィックスが作成される。
また、プレフィックスには任意の文字列を指定することができる。
追加登録
以下に、リソースを追加する手順を示す。
- まず、リソースエディタを開いて、プレフィックスの行を選択する。
 または、プロジェクトツリーにあるプレフィックス名を右クリックして、[既存のファイルの追加...]を選択する。
- [Add Files]ボタンを押下する。
- ファイル選択ダイアログ画面にて、リソースファイルを選択する。([Ctrl] + [A]キーで全選択できる)
※注意
プロジェクトツリーのプレフィックス(/)を右クリックして、[既存のファイルを追加]を選択して登録することもできる。
リソースの削除
以下に、リソースを削除する手順を示す。
- 削除するリソースの行を選択する。
- [削除]ボタンを押下して、ファイルの削除画面を表示する。
- ファイル名を確認して、[OK]ボタンを押下する。
※注意
[Delete file permanently]にチェックを入力して削除する場合、ファイルの実体も削除される。
プロジェクトツリーで削除するリソースを右クリックして、[Remove...]を選択して削除することもできる。
リソースの保存
メニューを選択してリソースファイルを上書き保存する。
[ファイル] - [<リソースファイル名>の保存]
ツールボタンの画像
ツールバーのツールボタンでは、アイコンとテキストが表示できる。
ツールボタンは常に固定サイズであり、アイコンの位置はテキストの左または下を指定する。
また、アイコンのサイズも指定できる。
 std::unique_ptr<QToolButton> pToolBtn = std::make_unique<QToolButton>();
 QIcon Icon = QIcon(style()->standardPixmap(QStyle::SP_TitleBarMenuButton));
 
 pToolBtn->setIcon(Icon);
 pToolBtn->setText(tr("Test"));
 pToolBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
アイコンのサイズを指定しない場合、原画像のサイズで表示する。
プッシュボタンとは異なり、スタイルを指定しない場合はアイコンのみを表示する。
下表に、ボタンスタイルの定義を示す。
※注意
ツールボタンは、レイアウトの伸縮に影響されずに同じサイズを保持する。
表. ボタンスタイルの定義(enum Qt::ToolButtonStyle)
| 定義 | 値 | 説明 | 
|---|---|---|
| ToolButtonIconOnly | 0 | アイコンのみ | 
| ToolButtonTextOnly | 1 | テキストのみ | 
| ToolButtonTextBesideIcon | 2 | アイコンの横にテキスト | 
| ToolButtonTextUnderIcon | 3 | アイコンの下にテキスト | 
| ToolButtonFollowStyle | 4 | スタイルに従う | 
リソースをテキストエディタで編集する
テキストエディタを使用すると、リソースファイルの内容を直接変更することができる。
プロジェクトツリーのリソースファイル(.qrc)を右クリックして、[テキストエディタで開く]を選択する。
 // リソースのテキストエディタでの編集例
 <RCC>
    <qresource prefix="/">
       <file>images/drag.png</file>
       <file>images/flower.png</file>
       <file>images/photo.png</file>
    </qresource>
 </RCC>
※注意
テキストエディタでファイルパスの設定を削除しても、ファイルの実体が削除されることはない。
リソースの使用例
リソース名の文字列の取得
プロジェクトツリーのプレフィックスの下にリソース名が表示されている。
リソース名を右クリックして、[パス ":~" をコピー]を選択する。
使用例 1
ソフトウェアのタイトルバーのアイコンとして使用する。
 // プロジェクトディレクトリ直下のimagesディレクトリ内にあるflower.pngファイル
 setWindowIcon(QIcon(":/images/flower.png"));
使用例 2
ラベルにセットして、画像として表示する。
 // プロジェクトディレクトリ直下のimagesディレクトリ内にあるphoto.pngファイル
 std::unique_ptr<QLabel> pLabel = std::make_unique<QLabel>();
 pLabel->setPixmap(QPixmap(":/images/photo.png"));
使用例 3
ドラッグ&ドロップにおいて、ドラッグ中の画像として使用する。
 // プロジェクトディレクトリ直下のimagesディレクトリ内にあるdrag.pngファイル
 std::unique_ptr<QDrag> pDrag = std::make_unique<QDrag>(this);
 qDrag->setPixmap(QPixmap(":/images/drag.png"));
使用例 3
サブメニューにアイコンを割り当てる手順を示す。
- Qt Designer画面下のアクションエディタを右クリックして、[編集...]を選択する。
- [アクションを編集]画面が開くので、[アイコン(I):]項目の[...]ボタンを押下してリソースに登録した画像ファイルを選択する。
 または、[▼]ボタンを押下して、[ファイルを選択...]から画像ファイルを選択することもできる。
- アクションエディタにて、アクションの左側に選択したアイコンが表示される。
使用例 4
ツールバーにアイコンを割り当てる手順を示す。
- ツールバーの追加は、Qt Designerに表示しているウィンドウを右クリックして、[ツールバーを追加]を選択する。
- ツールバーの追加後、アクションエディタから任意のアクションをツールバーへ、ドラッグ&ドロップする。
ステータスバー
ウインドウまたはダイアログにおいて、ステータスバーを表示する。
 #include <QStatusBar>
 
 QStatusBar StatusBar;
 StatusBar->showMessage(tr("ステータスを表示中..."));
ステータスバーにプログレスバーを表示する場合、以下のように記述する。
 #include <QStatusBar>
 
 QStatusBar StatusBar;
 QProgressBar bar;
 StatusBar->addWidget(&bar);
 
 bar.setMinimum(0);
 bar.setMaximum(100);
 for(int i = 0; i < 100; i++)
 {
    bar.setValue(i);
    // 以下に処理を記述
    // ...
 }
 
 StatusBar->removeWidget(&bar);
タブオーダーの設定
タブオーダーを無効化するには、ウインドウにある全てのウィジェットのタブオーダーを自分自身にする。
 QList<QWidget *>widgets = findChildren<QWidget *>();
 
 foreach(QWidget *widget, widgets)
 {
    widget->setTabOrder(widget, widget);
 }
イベント
リサイズイベント
resizeEventメソッドは、ウィジェットのサイズが変化した時に発生する。
 // Mainwindow.h
 
 // ... 略
 
 protected:
    void resizeEvent(QResizeEvent *event);
 // Mainwindow.cpp
 // ウインドウの幅と高さをデバッグ出力する
 void MainWindow::resizeEvent(QResizeEvent *event)
 {
    qDebug("Resize old=%dx%d,new=%dx%d", event->oldSize().width(), event->oldSize().height(), event->size().width(), event->size().height() );
 }
クローズイベント
closeEventメソッドは、タイトルバーの×ボタンを押下した時やcloseメソッドを実行する時に発生する。
 // Mainwindow.h
 
 // ...略
 
 protected:
    void closeEvent(QCloseEvent *event);
 
 private:
    bool checkQuit;
コンストラクタでスイッチ(変数checkQuit)を初期化する。
スイッチをtrueにする時、ウインドウを閉じる直前に確認メッセージのYes / Noメッセージボックスを表示する。
Noを選択する場合、終了処理を行わずに元の画面に戻る。
Yesを選択する場合、ウインドウを閉じて終了する。
 // Mainwindow.cpp
 
 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
 {
    checkQuit = true;
 }
 // Mainwindow.cpp 
 void MainWindow::closeEvent(QCloseEvent *event)
 {
    if(checkQuit)
    {
       int ret = QMessageBox::question(this, tr("終了確認"), tr("終了しますか?"));
       if(ret == QMessageBox::No)
       {
          event->ignore();
       }
    }
    event->accept();
 }
ウインドウおよびウィジェットの形状の変更
ウインドウおよびプッシュボタン等のウィジェットにおいて、形状を非矩形に変更する、および、背景を画像に変更する手順を記載する。
非矩形ウインドウ
まず、型抜き用画像を作成する。
型抜き用画像は、ウインドウの形状を表す部分を描画した画像のことであり、画像以外の箇所を透過ピクセルにする。
したがって、型抜き用画像は透過(アルファチャンネル)をサポートするフォーマットで保存する必要がある。
これは、Inkscape(ソフトウェア)を使用して、ウィンドウの形状を描画してPNG形式で保存する方法を推奨する。
次に、QMainWindowクラスまたはQDialogクラスのコンストラクタにおいて、以下のように記述する。
 MainWindow::MainWindow()
 {
    Qt::WindowFlags flags = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::FramelessWindowHint;
    setWindowFlags(flags);
    QPixmap pixmap("window.png");
    setMask(pixmap.mask());
 
    // 型抜き画像のサイズに合わせる
    resize( 300, 300 );
 }
ウインドウおよびウィジェットの外観の変更
例えば、ウインドウの背景色を変更する場合、QMainWindowクラスまたはQDialogクラスのコンストラクタにおいて、以下のように記述する。
 MainWindow::MainWindow()
 {
    // 背景色を設定する
    setStyleSheet("* {background-color : rgb( 255, 255, 128 )}");
 
    // 透過率を設定する
    setWindowOpacity(0.7);
 }
また、上記の変更は、Qt Designerから実行することもできる。
Qt Designer画面から、ウインドウまたはウィジェットの[プロパティ] - [styleSheet]項目を選択する。
[スタイルシートを変更]画面が開くので、[〜を追加]プルダウンから"background-color"と"Opacity"のQSSを追加する。
QSSをプロジェクト全体に適用する
一般的に、QSSの設定は各ウィジェットごとに設定するが、プロジェクト全体で共通のQSSを設定することもできる。
QSSの書式や構文は、CSSとほぼ同じである。
以下の例は、Qtに付属しているサンプルを参考にしている。
(<Qtのインストールディレクトリ>/Examples/Qt-<バージョン名>/widgets/widgets/stylesheetディレクトリ)
まず、MainWindow.qssファイルを作成する。
 // MainWindow.qss
 
 // QPushButtonの設定例
 QPushButton
 {
    background-color: #000088;
    border-color: #000044;
    border-style: solid;
    border-radius: 5;
 }
 
 QPushButton:hover
 {
   background-color: #880000;
 }
 
 QPushButton:pressed
 {
    background-color: #008800;
 }
次に、MainWindow.qssファイルをQApplicationクラスのsetStyleSheetメソッドに渡す。
QApplicationクラスのインスタンスはシングルトンとして生成されているため、そのインスタンスから実行する。
また、QSSファイルはリソースとして登録しておくとよい。
以下の例では、main関数でQSSファイルを読み込み、設定している。
QSSには、他にも様々なクラスやセレクタが存在する。
 #include "MainWindow.h"
 #include <QtCore>
 
 int main(int argc, char *argv[])
 {
    QApplication app(argc, argv);
 
    QFile file(":/qss/MainWindow.qss");
    file.open( QFile::ReadOnly );
    QString strStyles = QLatin1String(file.readAll());
 
    app.setStyleSheet( strStyles );
 
    MainWindow w;
    w.show();
 
    return app.exec();
 }
ウィンドウの背景を画像にする
QStyleクラスは、ウィンドウの外見を詳細に設定することができる。
QStyleクラスを継承した派生クラスを作成して、必要な項目のうち、比較的簡単に設定できる項目である。
QStyleクラスを継承した派生クラスは数種類用意されており、OSにより使用できるものが決まっている。
以下の例は、QStyleクラスを継承した派生クラスを作成している。
設定後、QMainWindowクラスまたはQDialogクラスのコンストラクタにおいて、以下のように記述して適用する。
 QApplication::setStyle(new MyStyle);  // MyStyleクラスは、QStyleクラスの派生クラス
ウィンドウの背景を画像にするには、まず、polishメソッドをオーバーライドする。
次に、画像を設定するためのsetTextureメソッド(メソッド名は任意)を作成する。
 #pragma once
 
 #include <QStyle>
 
 class MyStyle : public QStyle
 {
 public:
    MyStyle();
    ~MyStyle();
 
    void polish(QPalette &palette);
 
 private:
    static void setTexture(QPalette &palette, QPalette::ColorRole role, const QPixmap &pixmap);
 };
polishメソッドとsetTextureメソッドは、以下のように記述する。
背景画像は、型抜き画像としても使用することができる。したがって、背景用画像と型抜き用画像の2つを用意する必要はない。
 void MyStyle::polish(QPalette &palette)
 {
    QPixmap backgroundImage("window.png");
 
    setTexture(palette, QPalette::Window, backgroundImage);
 }
 
 void MyStyle::setTexture(QPalette &palette, QPalette::ColorRole role, const QPixmap &pixmap)
 {
    for(int i = 0; i < QPalette::NColorGroups; ++i)
    {
       QColor color = palette.brush(QPalette::ColorGroup(i), role).color();
       palette.setBrush(QPalette::ColorGroup(i), role, QBrush(color, pixmap));
    }
 }
ボタンのスタイルの変更
上記のセクションにも記述したpolishメソッドを使用して、ボタンのスタイルを変更することができる。
QPaletteクラスのコンストラクタでは、ボタンの色を定義する。
ウィンドウおよびコントロールの設定の詳細は、Qtの公式WebサイトのQPalleteリファレンスを参照すること。
 void MyStyle::polish(QPalette &palette)
 {
    QPixmap backgroundImage("window.png");
 
    // 追加部分 : ボタンスタイルを変更
    palette = QPalette(QColor(0, 0, 0, 50));
    palette.setBrush(QPalette::ButtonText, Qt::cyan);
 
    setTexture(palette, QPalette::Window, backgroundImage);
 }
また、ボタンの背景を画像に変更するには、以下のように記述する。
 void MyStyle::polish(QPalette &palette)
 {
    QPixmap backgroundImage("window.png");
 
    // 追加部分 : ボタンの背景を画像にする場合
    QPixmap buttonImage("button.png");
 
    // 追加部分 : ボタンを押下した時の背景
    QPixmap midImage = buttonImage;
 
    setTexture(palette, QPalette::Button, buttonImage);
    setTexture(palette, QPalette::Mid, midImage);
    setTexture(palette, QPalette::Window, backgroundImage);
 }
ウインドウおよびウィジェットの形状を変更する
ウインドウおよびウィジェットの形状を変更するには、ウィジェットの形状をプログラムで全て記述する必要がある。
例えば、ボタンの形状を変更する場合、外形を描画するだけではなく、
ボタンが立体的に見えるように陰影の描画、ボタン押下時の陰影の変化等も記述する必要がある。
(或いは、QSS + 背景画像で形状を変更できる可能性がある)
ウインドウおよびウィジェットの形状を記述するには、QStyleクラスを継承した派生クラスを作成して、
QStyleクラスのdrawPrimitiveメソッドをオーバーライドする。
以下の例では、ボタンの形状を変更している。
Qtに付属しているサンプルを参考にしている。
(<Qtのインストールディレクトリ>/Examples/Qt-<バージョン名>/widgets/widgets/styles/norwegianwoodstyle.cppファイル)
 void MyStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
 {
    switch(element)
    {
       // ウィジェットのPrimitiveの種類を指定する(ウィジェットの種類ではない)
       case PE_PanelButtonCommand:
          int delta = (option->state & State_MouseOver) ? 64 : 0;
          QColor slightlyOpaqueBlack(0, 0, 0, 63);
          QColor semiTransparentWhite(255, 255, 255, 127 + delta);
          QColor semiTransparentBlack(0, 0, 0, 127 - delta);
 
          int x, y, width, height;
          option->rect.getRect(&x, &y, &width, &height);
 
          // 以下の3行のみ追記する
          // ボタンの形状を楕円にする
          QPainterPath path;
          path.addEllipse(option->rect);
 
          // これ以降の記述はコピーである
          // ボタンの押下時と非押下時の陰影等を記述する
          // 様々な形状に対応している
          int radius = qMin(width, height) / 2;
 
          QBrush brush;
          bool darker;
 
          const QStyleOptionButton *buttonOption = qstyleoption_cast<const QStyleOptionButton *>(option);
          if(buttonOption && (buttonOption->features & QStyleOptionButton::Flat))
          {
             brush = option->palette.background();
             darker = (option->state & (State_Sunken | State_On));
          }
          else
          {
             if(option->state & (State_Sunken | State_On))
             {
                brush = option->palette.mid();
                darker = !(option->state & State_Sunken);
             }
             else
             {
                brush = option->palette.button();
                darker = false;
             }
          }
 
          painter->save();
          painter->setRenderHint(QPainter::Antialiasing, true);
          painter->fillPath(path, brush);
          if(darker)
          {
             painter->fillPath(path, slightlyOpaqueBlack);
          }
 
          int penWidth;
          if(radius < 10)
             penWidth = 3;
          else if (radius < 20)
             penWidth = 5;
          else
             penWidth = 7;
 
          QPen topPen(semiTransparentWhite, penWidth);
          QPen bottomPen(semiTransparentBlack, penWidth);
 
          if(option->state & (State_Sunken | State_On))
          {
             qSwap(topPen, bottomPen);
          }
 
          int x1 = x;
          int x2 = x + radius;
          int x3 = x + width - radius;
          int x4 = x + width;
 
          if(option->direction == Qt::RightToLeft)
          {
             qSwap(x1, x4);
             qSwap(x2, x3);
          }
 
          QPolygon topHalf;
          topHalf << QPoint(x1, y) << QPoint(x4, y) << QPoint(x3, y + radius) << QPoint(x2, y + height - radius) << QPoint(x1, y + height);
 
          painter->setClipPath(path);
          painter->setClipRegion(topHalf, Qt::IntersectClip);
          painter->setPen(topPen);
          painter->drawPath(path);
 
          QPolygon bottomHalf = topHalf;
          bottomHalf[0] = QPoint(x4, y + height);
 
          painter->setClipPath(path);
          painter->setClipRegion(bottomHalf, Qt::IntersectClip);
          painter->setPen(bottomPen);
          painter->drawPath(path);
 
          painter->setPen(option->palette.foreground().color());
          painter->setClipping(false);
          painter->drawPath(path);
 
          painter->restore();
 
          break;
       default:
          // ボタン以外はMotifStyleの形状を採用
          QMotifStyle::drawPrimitive(element, option, painter, widget);
 
          break;
    }
 }

