【メモ】Qt + Visual C++ 小ネタ

# 前書き
自分用の備忘録で、実装の小ネタ集です。

# 実装環境
    OS: Windows 10(20H2)
    Visual Studio: Professional 2019(Version 16.10.3)
    Qt: 5.15.2
    Qt Visual Studio Tools: 2.7.1.18

# ダイアログ
## アプリケーション終了時に確認ダイアログを表示させる
closeEventイベントをオーバーライドする。

~~~c++
void QtWidgetsApplication1::closeEvent(QCloseEvent* event)
{
    int res = QMessageBox::question(this, tr("quit"), tr("application quit ok?"));
    if (res == QMessageBox::No) {
        event->ignore();
    }
}
~~~

## アプリケーションのバージョン表示
~~~c++
void QtWidgetsApplication1::on_action_About_triggered()
{
    QMessageBox::about(this, tr("version information"), QString("%1 Version: %2").arg(QApplication::applicationName()).arg(QApplication::applicationVersion()));
}
~~~
QApplication::applicationVersion()では、リソースの「VS_VERSION_INFO」からバージョンが取得できる。

## Qtについての情報表示
~~~c++
void QtWidgetsApplication1::on_action_AboutQt_triggered()
{
    QMessageBox::aboutQt(this, tr("About Qt"));
}
~~~
![AboutQt.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541088/911da21a-e25d-7e5a-6e47-118e75dd69e5.jpeg)
このようなダイアログが表示される。

# コンボボックス
## 指定アイテムを選択不可にする
~~~c++
QStringList list = {"aaa", "bbb", "ccc" };
list.prepend(tr("Not set"));
ui.comboBox->addItems(list);
QStandardItemModel* model = qobject_cast<QStandardItemModel*>(ui.comboBox->model());
QModelIndex firstIndex = model->index(0, ui.comboBox->modelColumn(), ui.comboBox->rootModelIndex());
QStandardItem* firstItem = model->itemFromIndex(firstIndex);
firstItem->setEnabled(false);    // "Not set"を選択不可にする
~~~
![NotSet.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541088/c7685802-09f8-4b42-3229-07708ca968ef.jpeg)
*firstItem->setEnabled(false)* の代わりに*firstItem->setSelectable(false)* を使用した場合、マウスでは選択できないが、カーソルキーでは選択できてしまう。

# スタイルシート
## スタイルシートの設定を子ウィジェットに引き継がないようにする

親側のスタイルシート設定で、

```css
#オブジェクト名 {
    background-color:white;
}
```
「#～」でobjectNameを指定し、スタイルシートの適用範囲を限定する。

# SQLite関連
## 日時の扱い
SQLiteで*CURRENT_TIMESTAMP*などを使用した場合に取得される現在時刻は**UTC**。

https://qiita.com/TomK/items/132831ab45e2aba822a8#%E7%8F%BE%E5%9C%A8%E6%99%82%E5%88%BB%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B

こちらの記事を参考にさせていただきました。
データベースに格納された日時を取得する際、

```c++
QSqlDatabase db = QSqlDatabase::database(connectionName);
QSqlQuery query(db);
query.prepare("select id, date, time from hogehoge where user_id=:id");
query.bindValue(":id", user_id);
query.exec();

QDateTime datetime = QDateTime::fromString(query.value(1).toString() + QString(" ") + query.value(2).toString(), "yyyy-MM-dd HH:mm:ss");
qDebug() << datetime.toLocalTime().toString("yyyy/MM/dd hh:mm:ss");
```
この場合ローカルタイムではなく、UTCで表示されてしまう。

```c++
QDateTime datetime = QDateTime::fromString(query.value(1).toString() + QString(" ") + query.value(2).toString(), "yyyy-MM-dd HH:mm:ss");
datetime.setTimeSpec(Qt::UTC);

qDebug() << datetime.toLocalTime().toString("yyyy/MM/dd hh:mm:ss");
```
こうすると、ローカルタイムで表示される。

# Json
## Json形式のファイルを読み込む

```c++
QFile jsonFile("hogehoge.json");
if (jsonFile.open(QIODevice::ReadOnly)) {
    QJsonObject data(QJsonDocument::fromJson(jsonFile.readAll()).object());
}
```
Jsonファイルを指定して読み込む。
指定したJsonファイルが、

```json
{
    "foo": 10,
    "bar": "aaa"
}
```
というものであった場合、

```c++
data["foo"].toInt()
```
と指定することで[int型の数値を取り出すことができる](https://doc.qt.io/qt-5/qjsonvalue.html#toInt)。

## Json形式のファイルとして書き出す
```c++
QFile jsonFile("hogehoge.json");
if (jsonFile.open(QIODevice::WriteOnly)) {
    QJsonDocument saveDoc(data);
    jsonFile.write(saveDoc.toJson());
    jsonFile.close();
}
```

# デバッグログ
## デバッグログをファイル出力する
qInstallMessageHandlerを使用する。

```c++
void myMessageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg)
{
    OutputDebugStringW(QString(msg + "\n").toStdWString().c_str());     // VisualStudioの出力ウィンドウにも表示する
    QFile file("log.txt");
    if (!file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
        return;
    }
    QTextStream ts(&file);
    ts.setCodec(QTextCodec::codecForName("UTF-8"));
    ts << msg << endl;
}

int main(int argc, char **argv)
{
    qInstallMessageHandler(myMessageOutput);
    QApplication app(argc, argv);
    ...
    return app.exec();
}
```
https://doc.qt.io/qt-5/qtglobal.html#qInstallMessageHandler

# 共通言語ランタイムサポートについて
## OleInitialize() failedへの対応
プロジェクトのプロパティで「共通言語ランタイムサポート（/clr）」を有効にした際、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541088/c28d04c4-9cdb-e20f-ee9f-d4b3328ae285.png)
プログラム実行時に、

```shell
QWindowsContext: OleInitialize() failed:  "COM error 0xffffffff80010106 RPC_E_CHANGED_MODE (Unknown error 0x080010106)"
```
このようなエラーが発生してしまう（プログラムの実行自体はされる）。

main.cppのmain関数の直前に、

```c++
#include "QtWidgetsApplication1.h"
#include <QtWidgets/QApplication>
#using <system.dll>

[System::STAThread]
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QtWidgetsApplication1 w;
    w.show();
    return a.exec();
}
```
**[System::STAThread]**行を追加することで回避できた。

https://stackoverflow.com/questions/20525554/pyside-qt-could-not-initialize-ole-error-80010106

https://dobon.net/vb/dotnet/form/stathread.html

これらの記事を参考にさせていただいた。