Qt

提供:senooken JP Wiki

About

参考になる情報源がいくつかある。

Mastering Qt5という本があり、それを翻訳している人がいる。

General

License

情報源:

Qtには3種類のライセンス形態がある。

  1. 商用: 有償。不自由ソフトの開発用。
  2. GPLv3: 自由ソフト用。
  3. LGPLv3: 無償で不自由ソフトの開発に使用可能。ただし、ユーザーにQt部分のライブラリーを置換可能な手順・プロジェクトファイル群の提供が必要で、使用可能なモジュールの制限が多い。Stack Overflowや公式サイト (Open Source Development | Open Source License | Qt) 説明がわかりやすい。

LGPLv3の義務は以下。

  • Qtライブラリの再リンクメカニズムを提供する
  • ライセンスのコピーを提供し、Qtの使用を明示的に記載する
  • Qtソースコードのコピーを顧客が利用できるようにする
  • Qtソースコードの変更が独占的でないことを了承する
  • 「オープン」なコンシューマデバイスを作成する
  • デジタル著作権管理の条件に同意する(GPL FAQをご覧ください)
  • ソフトウェア特許を施行しようとするときは、特別な配慮が必要(FAQ

Business

OSSライセンスで開発後、商用ライセンスに移行する場合、不可能ではないが条件がある。

まず、Qtの営業に連絡する。商用ライセンスのモジュールは不自由ソフトなので、そこを維持するために、OSS部分を移行する必要があり、以降の支援の業務契約がおそらく必要。そうとう金額がかかるだろう。

Module

ライセンス形態ごとに使用可能なモジュールが変わる (Qt Features, Framework Essentials, Modules, Tools & Add-Ons)。これが個人的には大きい。

2023-12-13 Wed。

GPLv3で使用不能モジュール

  • Development Tools
    • Qt Quick Compiler Extensions
    • Qt Quick Ultralight Compiler
    • Qt Quick Ultralight Project Exporter
  • Framework Add-Ons
    • Qt Digital Advertising
    • Active Qt
    • Qt WebEngine
    • Qt Safe Renderer
    • Qt Quick Ultralite Controls
    • Qt Quick Ultralite
    • Non-automotive MCU Deployment Platforms
    • Automotive MCU Deployment Platforms
    • Qt Design Studio Bridge
    • Qt for Android Automotive OS Car Service API
    • Simulink support for Qt Design Studio
    • Boot to Qt
    • Qt for Python commercial add-ons

LGPLv3で使用不能なもの

  • GPLv3で使用不能なもの
  • Framework Add-Ons
    • Qt Wayland Compositor
    • Qt Charts
    • Qt Data Visualization
    • Qt Network Authorization
    • Qt Virtual Keyboard
    • Qt Safe Renderer
    • In-Vehicle Infotainment Reference UI
    • Qt Application Manager
    • Qt Device Utilities
    • Qt MQTT
    • Qt CoAP
    • Qt Design Studio Bridge
    • Qt Quick Calendar
    • Qt Quick TreeView n
    • Qt Quick Timeline
    • Qt Lottie Animation
    • Qt Quick 3D
    • Qt HTTP Server
    • Qt Quick 3D Physics
    • Qt gRPC
    • Qt Protobuf

使用不能なモジュールを見る限り、別にLGPLv3でも通常利用には問題ないように感じる。どうせ元々使いたいものはGPLv3でも使用不能だし。

Style

Qtでの開発のコーディング規約が気になったので調べた。

特に特徴的なものを以下に記載する。

  • 関数の波括弧のみ次の行。
  • メンバー変数はm_のプレフィクスでmixedCase。

Platform

iOS

出典: Qt for iOS | Qt 6.6

Xcodeが必須。それ以外はソースコードに手を付けなくてもビルドはできる。

Packaging

Qt Creator

出典: Qt Creator Manual

Qtの標準IDE。Qtの開発で非常に重要。

Keybind

よく使うショートカットキー、短絡キーのキー割当、キーバインドをメモしておく。

  • C-r: Run app.
  • C-b: Build app.

Tutorial

出典: Creating a Qt Widget Based Application | Qt Creator Manual

Other

MacのBackspace

出典: [QTCREATORBUG-26764] Backspace is not working in Mac Qt Creator 6.0.1 - Qt Bug Tracker

MacのQt Createorだと、Google日本語入力のIMEだとBackspaceやDeleteキーがうまく動作しない模様。

しかたないので、開発作業中だけデフォルトIMEのKotoeriを使う。

UIファイルの直編集

出典: qt creator - Hand editing .ui files inside Qt editor - Stack Overflow

あまりしないほうがいいが、したくなるときがある。

右クリック-[Open With]-[Plain Text Editor] を選ぶ。

Qt Designer

出典: Qt Designer Manual

Qt DesignerはQt WidgetsのGUIエディター。画面でUI部品を構築できて、UI部品をxmlにしてくれる。

Qt Widgetsでの画面開発をかなり簡単にしてくれる。Qt Widgetsの基本を学習したら、たぶんQt Designerを使ったほうがいい。

元々は専用のアプリだったが、今はQt Creatorに組み込まれている。

Connect the signals to the slots

出典: A Quick Start to Qt Designer | Qt Designer Manual

Qt Designerでウィジェットを右クリック-[Go to slot] を選ぶと、スロットを設定できる。

[Select signal] 画面で対象のsignalを選択して、該当slotの作成となる。

Menu

左上のツールバー?部分に [Type Here] と表示されている。ここにメニューの項目名を入力する (例: File)。

するとメニューが生成されて、メニュー項目に対して [Type Here] が表示される。同じようにここを入力すると (例: New)、メニューのサブ項目が生成される。そして、[Action Editor] にActionも追加される。

追加されたActionに対して、右クリック-[Go to slot]-[triggered()] を選ぶと、該当slot関数の雛形が生成される。

UI

Qt Designerでは.uiファイルで画面を定義する。これはXMLファイルになっていて「Qt Designer's UI File Format | Qt Designer Manual」がXML schema。

デフォルトのMainWindowクラスをみると、mainwindows.hに以下の記載がある。

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

Ui名前空間がuiファイルになっている。

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">

uiファイル側 (mainwindow.ui) には同名のクラス定義が上記のようにあり、基本的にuiファイルと一対一の関係になっている。

uiファイルはビルド時にuicツールでコンパイルされ、その際にui_mainwindow.hでうまく連動される模様。

わかりにくいが、Ui::MainWindowのUiの名前空間有無で2個のクラスがあり、Ui側はuiファイルのもの。

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , m_httpRequest(parent)
    , m_ui(new Ui::MainWindow)
 {
    m_ui->setupUi(this);
}

コンストラクターで上記のようにm_uiでUI側インスタンスを保持している。m_uiでuiのウィジェットにアクセスできる。

例えば、以下のような感じ。

void Notepad::newDocument()
{
    currentFile.clear();
    ui->textEdit->setText(QString());
}

ビルドディレクトリーに生成されるui_mainwindow.hを見ると、全ウィジェットがui->直下に存在する模様。

Other

QML

http://doc.qt.io/qt-6/qtqml-documents-structure.html

2種類から構成。

  1. import文
  2. rootオブジェクト宣言

importとオブジェクトは空行で分離。

エンコーディングはUTF-8。

HTTP

情報源:

HTTPサーバーへのリクエストは頻出処理。curlで自分で実装してもいいが、Qtで専用のクラス類が用意されているので使うといい。

具体的には以下の3クラスにお世話になる。

  • QNetworkAccessManager: このクラスは、アプリケーションが要求を送信し、応答を受信することを可能にします。
  • QNetworkRequest: このクラスは、送信するリクエストをすべての情報(ヘッダ、URL、データなど)と一緒に保持します。
  • QNetworkReply: このクラスは、ヘッダーとデータを持つQNetworkRequestクラスの結果を含みます。

基本はQNetworkAccessManagerが通信呼び出し。

get/postなどHTTPメソッドに相当するメソッドがある。これらは非同期。メソッドがQNetworkReply*を返す。このreplyオブジェクトが、読み込み状況に応じたsignalを発動するので、それにconnectで処理を割り当てることで、応答データにアクセスできる。

通信が完了すると最後にreplyFinishedのslot関数が共通で呼ばれる。

Download Data from URL

出典:

QImageで画像のURL参照は工夫が必要。

元ファイルを一度GETしてデータを取得してから参照する形になる。

  1. 該当URL (QUrl) にQNetworkRequest$get
  2. finishedのシグナルでデータを読み込む (pReply->readAll())。
  3. 読み込んだデータに対して、QPixmap$loadFromData()で読み込む。
  4. QLabel$setPixmap()

Layout

QScrollArea in QTabWidget

出典: How to add a QScrollArea as a tab in a QTabWidget | Qt Forum

QTabWidget内にQScrollAreaを配置すると、フィットしてくれない。設定箇所がわかりにくい。

TabWidgetを右クリック-[Choose Lay out] から任意のレイアウトを選ぶとフィットしてセンタリングされる。

その後、scrollAreaWidgetsContentsができる。こいつの直下にlayoutを指定することができる。

一度QWidgetを下に配置して、QScrollAreaを右クリックでレイアウトを追加して、QWidgetsを削除すればできる。余計なQWidgetsを1個省略できる。ややこしい。

Spacing

出典: Adjust Spacing and Margins between Widgets in Layout - Qt Wiki

BoxLayoutなどにウィジェットを配置していくと、ウィジェット間にスペースができる。

これはLayoutの以下で制御できる。

  • Spacing
  • ContentsMargins

ウィジェットのプロジェティーではない点に注意する。

Word Wrap

出典: Word Wrap of Text in QLabel - Qt Wiki

テキストの折返し。QLabelの場合、WordWrapプロパティーで制御する。デフォルトはfalse。

Child widget

基本的に、QWidget->layout()/QLayout->itemAt(N)->widget()を繰り返すことで、子ウィジェットにアクセスできるようになっている。

他に、QWidget::findChild/findChildrenとかで、名前がわかっているならそれで取得もできる。

Qt Widgets

出典: Qt Widgets 6.6.1

Qtの原点。昔ながらのC++デスクトップアプリ用のモジュール。古くから存在していて、一番安定している。C++のフル機能を利用できる。Qt Quickも悪くはないが、こちらはモバイルファースト。C++技術者ならC++の勉強のためにもQt Widgetsを使うのがいい。Qt QuickはC++をあまり使わなくて、C++の知識の流用・学習が利かない。

main.cpp

出典: Widgets Tutorial | Qt Widgets 6.6.1

Qt Widgetを使ったアプリは以下のmain.cppから始まるのが基本。

#include <QtWidgets>

// Include header files for application components.
// ...

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

    // Set up and show widgets.
    // ...

    return app.exec();
}

QtのアプリはQObjectクラスを全て継承しており、内部で連携している。

QApplicationでインスタンスを生成したら、それ以後にQObject派生クラスを生成したりすると、内部的に連携しているこれでアプリを作っていく感じになる。

Getting Started Programming with Qt Widgets

出典: Getting Started Programming with Qt Widgets | Qt Widgets 6.6.1

このチュートリアルが良くできている。この中身を理解できれば、それで基本的なアプリを作れると思う。

signal/slot

General

出典: Signals & Slots | Qt Core 6.6.1

Qtでイベントを処理するための重要な仕組み。

一般的に、GUIプログラミングでは、あるウィジェットの変更を、他のウィジェットに通知したいことがある。例えば、ユーザーがCloseボタンを選んだら、close関数を呼び出すなど。

多くのツールキットはコールバック (関数ポインター) を利用してこの種の通信を実現します。イベント発生時にこのコールバックを呼び出して、通知します。ただし、コールバックはあまり直感的ではなく、引数の型の保証などで問題が生じる可能性がある。

Qtでは、コールバックの代わりに、シグナル・スロットという仕組みを使用します。イベントが発生すると、シグナルが送信され、スロットはシグナルに応答して呼び出される関数です。どちらも独自に追加可能です。

connect(Object1, signal1, Object2, slog1) などのように、オブジェクトのスロット・シグナルをそれぞれ指定して連動させる。

シグナルとスロットはタイプセーフ、つまり関数シグネチャーの一致が必要。ただし、オプション引数なことが多いので、スロット側は実施は余分な引数を無視できることが多い。

QObjectとその派生クラスにはsignal/slotを含められる。

最小限のクラスは以下のような感じ。

#include <QObject>

class Counter : public QObject
{
    Q_OBJECT

public:
    Counter() { m_value = 0; }

    int value() const { return m_value; }

public slots:
    void setValue(int value);

signals:
    void valueChanged(int newValue);

private:
    int m_value;
};

signalは具体的には以下のようにemit <signal>で送信される。

void Counter::setValue(int value)
{
    if (value != m_value) {
        m_value = value;
        emit valueChanged(value);
    }
}

シグナルへのスロット接続は以下の通りに行う。

    Counter a, b;
    QObject::connect(&a, &Counter::valueChanged,
                     &b, &Counter::setValue);

    a.setValue(12);     // a.value() == 12, b.value() == 12
    b.setValue(48);     // a.value() == 12, b.value() == 48

slots named on_foo_bar are error prone

Qt Deisgnerで [Go to slot] で自動生成されるslotはon_[widget name]_[signal]() のような命名規則になっている。この命名規則だと自分でconnectで結び付けなくてもいい。ただ、Qt Creatorで以下の警告がでる。

slots named on_foo_bar are error prone [clazy-connect-by-name]

出典:

この規則は便利ではあるが、例えばウィジェット名を変更した場合に反映されないので問題になりやすいので警告が出る模様。

自分でconnectで指定するのがよいとか。後で理解できたところで対応する。

QWidget

出典: QWidget Class | Qt Widgets 6.6.1

Qt Widgetsのベースウィジェットクラス。Qt Widgetsは基本的にQWidgetを継承しており、このクラスのメンバー関数、メンバー変数は共通。何回も使用する重要なベースクラス。

Widget

出典: Widgets Classes | Qt Widgets 6.6.1

ウィジェット関係クラスが一覧されている。基本的にはここに掲載されているウィジェットでアプリを作る。

特によく使う重要なものを抜粋しておきたい。

  • QLabel: ラベル。テキスト入力の基本。Markdownとかいろいろできる。
  • QLineEdit: 1行入力欄。
  • QScrollArea: スクロール可能な領域。
  • QTabWidet: いわゆるタブペイン。
  • QWidget: ベースクラス。

Tab

Tabを実装する方法がいくつかある。

一般的なタブはQTabWidgetを使えば実現できる。通常はこれで問題ないだろう。

Tab Dialog Example | Qt Widgets 6.6.1」が例。

Box

出典

ウィジェットで頻出の (枠ありの) 箱型ウィジェット。QFrameが基本で、これを継承するQLabelも。QGroupBoxも似たような感じだが、こちらはRadioButtonなどのグループ化などで使う感じ。QGroupBoxはQFrameを継承していないので、枠線の調整などがしにくい。QFrame/QLabelを使うとよい。

QLabelにはWordWrapプロパティーがあって、親ウィジェットでワードラップに対応できるから、文字表示用だとQLabelがよさそう。親で設定すれば、いちいち子側で設定がいらなくなるだろうから。うーん…QLabelのネストはうまくいかない。何かいる模様。

Accordion/collapse

ボタンを押したら、返信画面が表示されるようなUIがほしい。いわゆるアコーディオン。

QToolBoxが似ている。QToolButtonとかも。Qt Designerのセクションとかもこれ。QTreeWidgetかQTreeViewを使っている可能性が高いとのこと。QTreeWidgetはテキストしか配置できない。

user interface - Is there a standard component for collapsible panel in Qt? - Stack Overflow」では、QPushButtonとQListViewで連動させる。これがシンプルとか。

QPushButtonとQFrameで自前でやるのがシンプル (c++ - How to make an expandable/collapsable section widget in Qt - Stack Overflow)。

他に、QTabWidgetでタブ部分のボタン押下で高さを変化させるとかでもできるか?

qt - QTabWidget how to hide pane only? - Stack Overflow」が参考になる。

        this->setMaximumHeight(this->tabBar()->height());
        this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);

tabWidget->setMaximumHeight(tabWidget->tabBar()->height());でタブ部分の高さにする。

Qt Core

QSettings

About

出典: QSettings Class | Qt Core 6.6.1

アプリ設定の保存のためのクラス。フォームの内容などを、次回のアプリ起動時に維持可能になる。

まず、引数に著者名、製品名でインスタンスを作成する。

    QSettings settings("MySoft", "Star Runner");

その後、setValue()でキーバリュー形式で値を保存する。

    settings.setValue("editor/wrapMargin", 68);

保存した値はvalue()で参照できる。

    int margin = settings.value("editor/wrapMargin").toInt();

値がなければ、nullになる。デフォルト値を第2引数で指定もできる。

設定の数が多い場合、beginGroupとendGroupでグループを作ることができる。/の階層を毎回書かなくて済むというだけ。他に、配列での読み書きもある。

Restoring the State of a GUI Application

一般的なGUIアプリの場合、設定の読み書きの実装のお決まりパターンがある。

まず以下のような読み書き用の関数を用意しておく。

void MainWindow::writeSettings()
{
    QSettings settings("Moose Soft", "Clipper");

    settings.beginGroup("MainWindow");
    settings.setValue("geometry", saveGeometry());
    settings.endGroup();
}

void MainWindow::readSettings()
{
    QSettings settings("Moose Soft", "Clipper");

    settings.beginGroup("MainWindow");
    const auto geometry = settings.value("geometry", QByteArray()).toByteArray();
    if (geometry.isEmpty())
        setGeometry(200, 200, 400, 400);
    else
        restoreGeometry(geometry)
    settings.endGroup();
}

その後、これらをコンストラクターとウィンドウのcloseEventに設定する。

MainWindow::MainWindow()
{
    ...
    readSettings();
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    if (userReallyWantsToQuit()) {
        writeSettings();
        event->accept();
    } else {
        event->ignore();
    }
}

JSON

出典:

QtでJSON用のクラスがいくつかある。

QJsonDocument::fromJson(). toJson() のstatic関数で、文字列と相互変換できる。基本はこれだけ。

QFile file("[jsonファイルのパス]");
file.open(QFile::ReadOnly);
QTextStream in(&file);

QJsonDocument jsonDoc = QJsonDocument::fromJson(in.readAll().toUtf8());
    ///JSONファイルから読み込み
QJsonObject obj = jsonDoc.object();
QStringList keys = obj.keys();
    ///全てのキーを取得
foreach(QString key, keys){
    qDebug() << "key = " << key;
    QString value = obj.value(key).toString();
    qDebug() << "value = " << calue;
}

file.close();

QJsonDocument.object()でQJsonObjectを取得できる。

arrayも。

Container

Qtで用意されているコンテナークラス群がある。

QHash (QHash Class | Qt Core 6.6.1): 順序不定の連想配列。std::unordered_map相当早い。

QMap (QMap Class | Qt Core 6.6.1): 順序ソートの連想配列。std::map相当。

順序が重要でなければ、Hashがいい。順序ソートに意味があるならQMap

Qt GUI

QPixmap

出典: QPixmap Class | Qt GUI 6.6.1

Qtの画像処理関係。

  • QImage: I/O。
  • QPixmap: 表示用。
  • QBitmap: QPixmapを継承したヘルパー。
  • QPicture: QPainterの描画用。

QPixmapはQLabelなどの表示が簡単にできるようになっている。

WebP

MacでWebP画像の表示がうまくできなくて困った。

WebPはサードパーティーライブラリとして同梱されている。qtimageformats。

ビルド設定を追加する必要があるらしい。オンラインインストーラーで追加インストールが必要かもしれない。

Qt/6.6.1/macos/plugins/imageformats に以下のライブラリーがあるがwebpはない。

libqgif.dylib libqico.dylib libqjpeg.dylib libqsvg.dylib

/Users/user/Qt/QtDesignStudio/qt6_design_studio_reduced_version/plugins/imageformatsにlibqwebp.dylibがあるのでこれを格納すればいいらしい。

これを格納するだけでwebpが対応形式に追加された。

    qDebug() <<  QImageReader::supportedImageFormats();
    qDebug() <<  QImageWriter::supportedImageFormats();
QList("bmp", "cur", "gif", "ico", "jpeg", "jpg", "pbm", "pgm", "png", "ppm", "svg", "svgz", "webp", "xbm", "xpm")
QList("bmp", "cur", "ico", "jpeg", "jpg", "pbm", "pgm", "png", "ppm", "webp", "xbm", "xpm")