「Qt」の版間の差分
(UI) |
(カテゴリー追加。) |
||
(同じ利用者による、間の24版が非表示) | |||
110行目: | 110行目: | ||
== Platform == | == Platform == | ||
* [https://doc.qt.io/qt-6/mobiledevelopment.html Mobile Development | Qt 6.6] | |||
=== iOS === | === iOS === | ||
115行目: | 117行目: | ||
Xcodeが必須。それ以外はソースコードに手を付けなくてもビルドはできる。 | Xcodeが必須。それ以外はソースコードに手を付けなくてもビルドはできる。 | ||
=== Packaging === | |||
* [https://doc.qt.io/qt-6/deployment.html Deploying Qt Applications | Qt 6.6] | |||
* [https://doc.qt.io/qt-6/windows-deployment.html Qt for Windows - Deployment | Qt 6.6] | |||
* [https://doc.qt.io/qt-6/macos-deployment.html Qt for macOS - Deployment | Qt 6.6] | |||
* [https://doc.qt.io/qt-6/linux-deployment.html Qt for Linux/X11 - Deployment | Qt 6.6] | |||
* [https://doc.qt.io/qt-6/cmake-deployment.html Deployment | Build with CMake 6.6.1] | |||
* [https://doc.qt.io/qt-6/android-deploy-qt-tool.html The androiddeployqt Tool | Qt Core 6.6.1] | |||
* [https://doc.qt.io/qt-6/deployment-plugins.html Deploying Plugins | Qt 6.6] | |||
* [https://doc.qt.io/qt-6/qtquick-deployment.html Deploying QML Applications | Qt 6.6] | |||
* [https://doc.qt.io/qt-6/deployment-android.html Deploying an Application on Android | Qt 6.6] | |||
* [https://doc.qt.io/qt-6/android-publishing-to-googleplay.html Publishing to Google Play | Qt 6.6] | |||
* [https://doc.qt.io/qt-6/qtpurchasing-appstore.html Registering Products in App Store | Qt 6.6] | |||
== Qt Creator == | == Qt Creator == | ||
120行目: | 136行目: | ||
Qtの標準IDE。Qtの開発で非常に重要。 | Qtの標準IDE。Qtの開発で非常に重要。 | ||
=== Keybind === | |||
よく使うショートカットキー、短絡キーのキー割当、キーバインドをメモしておく。 | |||
* C-r: Run app. | |||
* C-b: Build app. | |||
=== Tutorial === | === Tutorial === | ||
133行目: | 155行目: | ||
しかたないので、開発作業中だけデフォルトIMEのKotoeriを使う。 | しかたないので、開発作業中だけデフォルトIMEのKotoeriを使う。 | ||
== | ==== UIファイルの直編集 ==== | ||
出典: [https:// | 出典: [https://stackoverflow.com/questions/12147878/hand-editing-ui-files-inside-qt-editor qt creator - Hand editing .ui files inside Qt editor - Stack Overflow]。 | ||
あまりしないほうがいいが、したくなるときがある。 | |||
右クリック-[Open With]-[Plain Text Editor] を選ぶ。 | |||
==Qt Designer== | ==Qt Designer== | ||
241行目: | 221行目: | ||
ビルドディレクトリーに生成されるui_mainwindow.hを見ると、全ウィジェットがui->直下に存在する模様。 | ビルドディレクトリーに生成されるui_mainwindow.hを見ると、全ウィジェットがui->直下に存在する模様。 | ||
==HTTP== | == Other == | ||
=== QML === | |||
http://doc.qt.io/qt-6/qtqml-documents-structure.html | |||
2種類から構成。 | |||
# import文 | |||
# rootオブジェクト宣言 | |||
importとオブジェクトは空行で分離。 | |||
エンコーディングはUTF-8。 | |||
=== HTTP === | |||
情報源: | 情報源: | ||
255行目: | 249行目: | ||
*QNetworkRequest: このクラスは、送信するリクエストをすべての情報(ヘッダ、URL、データなど)と一緒に保持します。 | *QNetworkRequest: このクラスは、送信するリクエストをすべての情報(ヘッダ、URL、データなど)と一緒に保持します。 | ||
*QNetworkReply: このクラスは、ヘッダーとデータを持つQNetworkRequestクラスの結果を含みます。 | *QNetworkReply: このクラスは、ヘッダーとデータを持つQNetworkRequestクラスの結果を含みます。 | ||
基本はQNetworkAccessManagerが通信呼び出し。 | |||
get/postなどHTTPメソッドに相当するメソッドがある。これらは非同期。メソッドがQNetworkReply*を返す。このreplyオブジェクトが、読み込み状況に応じたsignalを発動するので、それにconnectで処理を割り当てることで、応答データにアクセスできる。 | |||
通信が完了すると最後にreplyFinishedのslot関数が共通で呼ばれる。 | |||
=== Download Data from URL === | |||
出典: | |||
* [https://wiki.qt.io/Download_Data_from_URL Download Data from URL - Qt Wiki] | |||
* [https://www.qtcentre.org/threads/1483-Qt4-How-to-load-Url-image-into-QImage [Qt4] How to load Url image into QImage?] | |||
* [https://izumisy.work/entry/Qt%E3%81%A7%E3%83%A1%E3%83%8B%E3%83%A5%E3%83%BC%E3%81%AB%E3%83%95%E3%82%A1%E3%83%93%E3%82%B3%E3%83%B3%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%97%E3%81%9F%E3%81%84%E3%81%A8%E3%81%8D%E3%81%AE%E3%83%A1%E3%83%A2 Qtでメニューにファビコンを表示したいときのメモ - Runner in the High] | |||
QImageで画像のURL参照は工夫が必要。 | |||
元ファイルを一度GETしてデータを取得してから参照する形になる。 | |||
# 該当URL (QUrl) にQNetworkRequest$get | |||
# finishedのシグナルでデータを読み込む (pReply->readAll())。 | |||
# 読み込んだデータに対して、QPixmap$loadFromData()で読み込む。 | |||
# QLabel$setPixmap() | |||
== Layout == | |||
=== QScrollArea in QTabWidget === | |||
出典: [https://forum.qt.io/topic/144775/how-to-add-a-qscrollarea-as-a-tab-in-a-qtabwidget/4 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 === | |||
出典: [https://wiki.qt.io/Adjust_Spacing_and_Margins_between_Widgets_in_Layout Adjust Spacing and Margins between Widgets in Layout - Qt Wiki]。 | |||
BoxLayoutなどにウィジェットを配置していくと、ウィジェット間にスペースができる。 | |||
これはLayoutの以下で制御できる。 | |||
* Spacing | |||
* ContentsMargins | |||
ウィジェットのプロジェティーではない点に注意する。 | |||
=== Word Wrap === | |||
出典: [https://wiki.qt.io/Word_Wrap_of_Text_in_QLabel Word Wrap of Text in QLabel - Qt Wiki]。 | |||
テキストの折返し。QLabelの場合、WordWrapプロパティーで制御する。デフォルトはfalse。 | |||
=== Child widget === | |||
基本的に、QWidget->layout()/QLayout->itemAt(N)->widget()を繰り返すことで、子ウィジェットにアクセスできるようになっている。 | |||
他に、QWidget::findChild/findChildrenとかで、名前がわかっているならそれで取得もできる。 | |||
==Qt Widgets== | |||
出典: [https://doc.qt.io/qt-6/qtwidgets-index.html Qt Widgets 6.6.1]。 | |||
Qtの原点。昔ながらのC++デスクトップアプリ用のモジュール。古くから存在していて、一番安定している。C++のフル機能を利用できる。Qt Quickも悪くはないが、こちらはモバイルファースト。C++技術者ならC++の勉強のためにもQt Widgetsを使うのがいい。Qt QuickはC++をあまり使わなくて、C++の知識の流用・学習が利かない。 | |||
===main.cpp=== | |||
出典: [https://doc.qt.io/qt-6/widgets-tutorial.html 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=== | |||
出典: [https://doc.qt.io/qt-6/qtwidgets-tutorials-notepad-example.html Getting Started Programming with Qt Widgets | Qt Widgets 6.6.1]。 | |||
このチュートリアルが良くできている。この中身を理解できれば、それで基本的なアプリを作れると思う。 | |||
=== signal/slot === | |||
==== General ==== | |||
出典: [https://doc.qt.io/qt-6/signalsandslots.html Signals & Slots | Qt Core 6.6.1]。 | |||
== | Qtでイベントを処理するための重要な仕組み。 | ||
http://doc.qt.io/qt-6/ | |||
一般的に、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] | |||
出典: | |||
* [https://stackoverflow.com/questions/75384792/how-may-i-fix-my-error-prone-on-foo-bar-slots qt - How may I fix my error prone on_foo_bar slots - Stack Overflow] | |||
* [https://forum.qt.io/topic/129896/what-does-slots-named-on_foo_bar-are-error-prone-mean what does 'Slots named on_foo_bar are error prone' mean? | Qt Forum] | |||
* [https://github.com/KDE/clazy/blob/master/docs/checks/README-connect-by-name.md clazy/docs/checks/README-connect-by-name.md at master · KDE/clazy · GitHub] | |||
* [https://doc.qt.io/qt-6/qobject.html#auto-connection QObject Class | Qt Core 6.6.1] | |||
* [https://doc.qt.io/qt-6/designer-using-a-ui-file.html#widgets-and-dialogs-with-auto-connect Using a Designer UI File in Your C++ Application | Qt Designer Manual] | |||
この規則は便利ではあるが、例えばウィジェット名を変更した場合に反映されないので問題になりやすいので警告が出る模様。 | |||
自分でconnectで指定するのがよいとか。後で理解できたところで対応する。 | |||
=== QWidget === | |||
出典: [https://doc.qt.io/qt-6/qwidget.html QWidget Class | Qt Widgets 6.6.1]。 | |||
Qt Widgetsのベースウィジェットクラス。Qt Widgetsは基本的にQWidgetを継承しており、このクラスのメンバー関数、メンバー変数は共通。何回も使用する重要なベースクラス。 | |||
=== Widget === | |||
出典: [https://doc.qt.io/qt-6/widget-classes.html Widgets Classes | Qt Widgets 6.6.1]。 | |||
ウィジェット関係クラスが一覧されている。基本的にはここに掲載されているウィジェットでアプリを作る。 | |||
特によく使う重要なものを抜粋しておきたい。 | |||
* QLabel: ラベル。テキスト入力の基本。Markdownとかいろいろできる。 | |||
* QLineEdit: 1行入力欄。 | |||
* QScrollArea: スクロール可能な領域。 | |||
* QTabWidet: いわゆるタブペイン。 | |||
* QWidget: ベースクラス。 | |||
=== Tab === | |||
Tabを実装する方法がいくつかある。 | |||
* QTabBar: タブ部分のみのウィジェット。 | |||
* QStackedWidget: タブの下部分のウィジェット? | |||
* QTabWidget ([https://doc.qt.io/qt-6/qtabwidget.html QTabWidget Class | Qt Widgets 6.6.1]): タブ部分+タブ別のウィジェット。 | |||
* QToolBox: クリックすると上下に展開されるやつ ([https://www.youtube.com/watch?v=cqT9oZ0j8TU PyQt5 Tutorial | How to group your widgets with QToolBox class - YouTube])。タブというかボタンに近い。たまに設定画面などでこういうUIがある。 | |||
一般的なタブはQTabWidgetを使えば実現できる。通常はこれで問題ないだろう。 | |||
「[https://doc.qt.io/qt-6/qtwidgets-dialogs-tabdialog-example.html Tab Dialog Example | Qt Widgets 6.6.1]」が例。 | |||
=== Box === | |||
出典 | |||
* [https://doc.qt.io/qt-6/qframe.html#Shadow-enum QFrame Class | Qt Widgets 6.6.1] | |||
* [https://doc.qt.io/qt-6/qgroupbox.html#details QGroupBox Class | Qt Widgets 6.6.1] | |||
ウィジェットで頻出の (枠ありの) 箱型ウィジェット。QFrameが基本で、これを継承するQLabelも。QGroupBoxも似たような感じだが、こちらはRadioButtonなどのグループ化などで使う感じ。QGroupBoxはQFrameを継承していないので、枠線の調整などがしにくい。QFrame/QLabelを使うとよい。 | |||
QLabelにはWordWrapプロパティーがあって、親ウィジェットでワードラップに対応できるから、文字表示用だとQLabelがよさそう。親で設定すれば、いちいち子側で設定がいらなくなるだろうから。うーん…QLabelのネストはうまくいかない。何かいる模様。 | |||
=== Accordion/collapse === | |||
* [https://stackoverflow.com/questions/32476006/how-to-make-an-expandable-collapsable-section-widget-in-qt c++ - How to make an expandable/collapsable section widget in Qt - Stack Overflow] | |||
* [https://github.com/MichaelVoelkel/qt-collapsible-section GitHub - MichaelVoelkel/qt-collapsible-section: Basic collapsible section for QT] | |||
* [https://github.com/crapp/qaccordion GitHub - crapp/qaccordion: An Accordion Widget for the Qt application framework] | |||
* [https://stackoverflow.com/questions/18575656/with-qtoolbox-which-setting-to-have-page-be-only-its-content-size c++ - With QToolBox, which setting to have page be only its content size? - Stack Overflow] | |||
* [https://forum.qt.io/topic/111439/qtreeview-from-qlistview-from-qabstractlistmodel QTreeView from QListView from QAbstractListModel | Qt Forum] | |||
* [https://forum.qt.io/topic/127615/how-can-i-expand-and-collapse-part-of-my-form-with-qpropertyanimation/5 How can i expand and collapse part of my form with QPropertyAnimation | Qt Forum] | |||
ボタンを押したら、返信画面が表示されるようなUIがほしい。いわゆるアコーディオン。 | |||
QToolBoxが似ている。QToolButtonとかも。Qt Designerのセクションとかもこれ。QTreeWidgetかQTreeViewを使っている可能性が高いとのこと。QTreeWidgetはテキストしか配置できない。 | |||
「[https://stackoverflow.com/questions/11077793/is-there-a-standard-component-for-collapsible-panel-in-qt user interface - Is there a standard component for collapsible panel in Qt? - Stack Overflow]」では、QPushButtonとQListViewで連動させる。これがシンプルとか。 | |||
QPushButtonとQFrameで自前でやるのがシンプル ([https://stackoverflow.com/questions/32476006/how-to-make-an-expandable-collapsable-section-widget-in-qt c++ - How to make an expandable/collapsable section widget in Qt - Stack Overflow])。 | |||
他に、QTabWidgetでタブ部分のボタン押下で高さを変化させるとかでもできるか? | |||
「[https://stackoverflow.com/questions/19048021/qtabwidget-how-to-hide-pane-only 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 ==== | |||
出典: [https://doc.qt.io/qt-6/qsettings.html 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 === | |||
出典: | |||
* [https://doc.qt.io/qt-6/json.html JSON Support in Qt | Qt Core 6.6.1] | |||
* [http://qt-log.open-memo.net/sub/other__parse_json.html JSONファイルでデータを簡単に扱う | QT && C++] | |||
QtでJSON用のクラスがいくつかある。 | |||
* QJsonObject: QtのJSON表現。 | |||
* QJsonDocument ([https://doc.qt.io/qt-6/qjsondocument.html QJsonDocument Class | Qt Core 6.6.1]): 文書として相互処理するためのクラス。 | |||
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 ([https://doc.qt.io/qt-6/qhash.html QHash Class | Qt Core 6.6.1]): 順序不定の連想配列。std::unordered_map相当早い。 | |||
QMap ([https://doc.qt.io/qt-6/qmap.html QMap Class | Qt Core 6.6.1]): 順序ソートの連想配列。std::map相当。 | |||
順序が重要でなければ、Hashがいい。順序ソートに意味があるならQMap | |||
== Qt GUI == | |||
=== QPixmap === | |||
出典: [https://doc.qt.io/qt-6/qpixmap.html QPixmap Class | Qt GUI 6.6.1]。 | |||
Qtの画像処理関係。 | |||
* QImage: I/O。 | |||
* QPixmap: 表示用。 | |||
* QBitmap: QPixmapを継承したヘルパー。 | |||
* QPicture: QPainterの描画用。 | |||
QPixmapはQLabelなどの表示が簡単にできるようになっている。 | |||
=== WebP === | |||
* [https://doc.qt.io/qt-6/qtimageformats-index.html Qt Image Formats 6.6.1] | |||
* [https://github.com/spillerrec/qt-webp-plugin GitHub - spillerrec/qt-webp-plugin: Adds WebP support to Qt applications] | |||
* [https://forum.qt.io/topic/143383/problem-when-trying-to-use-webp-in-the-app-s-final-executable-qt-6-4 Problem when trying to use webp in the app's final executable (Qt 6.4) | Qt Forum] | |||
* [https://github.com/qt/qtimageformats GitHub - qt/qtimageformats: Additional Image Format plugins for Qt] | |||
MacでWebP画像の表示がうまくできなくて困った。 | |||
WebPはサードパーティーライブラリとして同梱されている。qtimageformats。 | |||
ビルド設定を追加する必要があるらしい。オンラインインストーラーで追加インストールが必要かもしれない。 | |||
Qt/6.6.1/macos/plugins/imageformats に以下のライブラリーがあるがwebpはない。 | |||
<code>libqgif.dylib | |||
libqico.dylib | |||
libqjpeg.dylib | |||
libqsvg.dylib</code> | |||
/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") | |||
[[Category:IT]] |
2024年9月12日 (木) 09:35時点における最新版
About
参考になる情報源がいくつかある。
- GitHub - PacktPublishing/Mastering-Qt-5: Code repository for Mastering Qt 5 published by Packt
- Mastering Qt5に沿って勉強するページ | taro3.github.io
- examples - qt/qtbase.git - Qt Base (Core, Gui, Widgets, Network, ...): Qtのチュートリアルやサンプルの格納場所。
Mastering Qt5という本があり、それを翻訳している人がいる。
General
License
情報源:
- Qt - Obligations of the GPL and LGPL
- Open Source Development | Open Source License | Qt
- Qt ライセンスについて #Qt - Qiita
- ios - Does App Store accept Qt app linked with QT Library LGPLv3 - Stack Overflow
Qtには3種類のライセンス形態がある。
- 商用: 有償。不自由ソフトの開発用。
- GPLv3: 自由ソフト用。
- LGPLv3: 無償で不自由ソフトの開発に使用可能。ただし、ユーザーにQt部分のライブラリーを置換可能な手順・プロジェクトファイル群の提供が必要で、使用可能なモジュールの制限が多い。Stack Overflowや公式サイト (Open Source Development | Open Source License | Qt) 説明がわかりやすい。
LGPLv3の義務は以下。
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での開発のコーディング規約が気になったので調べた。
- Member variable naming convention in Qt and the QtDN wiki | Qt Forum
- Qt Coding Style - Qt Wiki
- Coding Conventions - Qt Wiki
- c++ - Qt naming convention for variables and getters so they don't collide? - Stack Overflow
- The Property System | Qt Core 6.6.1
特に特徴的なものを以下に記載する。
- 関数の波括弧のみ次の行。
- メンバー変数はm_のプレフィクスでmixedCase。
Platform
iOS
出典: Qt for iOS | Qt 6.6。
Xcodeが必須。それ以外はソースコードに手を付けなくてもビルドはできる。
Packaging
- Deploying Qt Applications | Qt 6.6
- Qt for Windows - Deployment | Qt 6.6
- Qt for macOS - Deployment | Qt 6.6
- Qt for Linux/X11 - Deployment | Qt 6.6
- Deployment | Build with CMake 6.6.1
- The androiddeployqt Tool | Qt Core 6.6.1
- Deploying Plugins | Qt 6.6
- Deploying QML Applications | Qt 6.6
- Deploying an Application on Android | Qt 6.6
- Publishing to Google Play | Qt 6.6
- Registering Products in App Store | Qt 6.6
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種類から構成。
- import文
- rootオブジェクト宣言
importとオブジェクトは空行で分離。
エンコーディングはUTF-8。
HTTP
情報源:
- HTTPデータの送受信 | taro3.github.io
- c++ - How do I write a Qt HTTP GET request? - Stack Overflow
- HTTP Client | Qt Network 6.6.1
HTTPサーバーへのリクエストは頻出処理。curlで自分で実装してもいいが、Qtで専用のクラス類が用意されているので使うといい。
具体的には以下の3クラスにお世話になる。
- QNetworkAccessManager: このクラスは、アプリケーションが要求を送信し、応答を受信することを可能にします。
- QNetworkRequest: このクラスは、送信するリクエストをすべての情報(ヘッダ、URL、データなど)と一緒に保持します。
- QNetworkReply: このクラスは、ヘッダーとデータを持つQNetworkRequestクラスの結果を含みます。
基本はQNetworkAccessManagerが通信呼び出し。
get/postなどHTTPメソッドに相当するメソッドがある。これらは非同期。メソッドがQNetworkReply*を返す。このreplyオブジェクトが、読み込み状況に応じたsignalを発動するので、それにconnectで処理を割り当てることで、応答データにアクセスできる。
通信が完了すると最後にreplyFinishedのslot関数が共通で呼ばれる。
Download Data from URL
出典:
- Download Data from URL - Qt Wiki
- [Qt4 How to load Url image into QImage?]
- Qtでメニューにファビコンを表示したいときのメモ - Runner in the High
QImageで画像のURL参照は工夫が必要。
元ファイルを一度GETしてデータを取得してから参照する形になる。
- 該当URL (QUrl) にQNetworkRequest$get
- finishedのシグナルでデータを読み込む (pReply->readAll())。
- 読み込んだデータに対して、QPixmap$loadFromData()で読み込む。
- 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]
出典:
- qt - How may I fix my error prone on_foo_bar slots - Stack Overflow
- what does 'Slots named on_foo_bar are error prone' mean? | Qt Forum
- clazy/docs/checks/README-connect-by-name.md at master · KDE/clazy · GitHub
- QObject Class | Qt Core 6.6.1
- Using a Designer UI File in Your C++ Application | Qt Designer Manual
この規則は便利ではあるが、例えばウィジェット名を変更した場合に反映されないので問題になりやすいので警告が出る模様。
自分で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を実装する方法がいくつかある。
- QTabBar: タブ部分のみのウィジェット。
- QStackedWidget: タブの下部分のウィジェット?
- QTabWidget (QTabWidget Class | Qt Widgets 6.6.1): タブ部分+タブ別のウィジェット。
- QToolBox: クリックすると上下に展開されるやつ (PyQt5 Tutorial | How to group your widgets with QToolBox class - YouTube)。タブというかボタンに近い。たまに設定画面などでこういうUIがある。
一般的なタブはQTabWidgetを使えば実現できる。通常はこれで問題ないだろう。
「Tab Dialog Example | Qt Widgets 6.6.1」が例。
Box
出典
ウィジェットで頻出の (枠ありの) 箱型ウィジェット。QFrameが基本で、これを継承するQLabelも。QGroupBoxも似たような感じだが、こちらはRadioButtonなどのグループ化などで使う感じ。QGroupBoxはQFrameを継承していないので、枠線の調整などがしにくい。QFrame/QLabelを使うとよい。
QLabelにはWordWrapプロパティーがあって、親ウィジェットでワードラップに対応できるから、文字表示用だとQLabelがよさそう。親で設定すれば、いちいち子側で設定がいらなくなるだろうから。うーん…QLabelのネストはうまくいかない。何かいる模様。
Accordion/collapse
- c++ - How to make an expandable/collapsable section widget in Qt - Stack Overflow
- GitHub - MichaelVoelkel/qt-collapsible-section: Basic collapsible section for QT
- GitHub - crapp/qaccordion: An Accordion Widget for the Qt application framework
- c++ - With QToolBox, which setting to have page be only its content size? - Stack Overflow
- QTreeView from QListView from QAbstractListModel | Qt Forum
- How can i expand and collapse part of my form with QPropertyAnimation | Qt Forum
ボタンを押したら、返信画面が表示されるような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用のクラスがいくつかある。
- QJsonObject: QtのJSON表現。
- QJsonDocument (QJsonDocument Class | Qt Core 6.6.1): 文書として相互処理するためのクラス。
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
- Qt Image Formats 6.6.1
- GitHub - spillerrec/qt-webp-plugin: Adds WebP support to Qt applications
- Problem when trying to use webp in the app's final executable (Qt 6.4) | Qt Forum
- GitHub - qt/qtimageformats: Additional Image Format plugins for Qt
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")