Qt の QUndoStack の挙動
ポエム
Qt には Undo framework があり、 QUndoStack
に QUndoCommand
を積んでいけば、いい感じに undo, redo できるようになる。
このときメモリが解放されるのかなど若干動作に癖というか、細かい部分の挙動の記述が少ないので記事にしておく。
挙動の確認は動かすのが面倒なので実際のコードを見た。ヘルプ書いてあっても英語だしあんま変わんないんだよね。
メモリ解放
QUndoStack
は以下のときにメモリ解放をしている。
push
時に、 redo 側に残されたコマンド- デストラクタ実行時に登録されているコマンド
つまり自前で QUndoCommand
のメモリ管理はせずに QUndoStack
に突っ込んでおけば良い。
push 時の挙動
確信が無かったので調べたけど、 QUndoStack
は QUndoCommand
を push
した際に一発 redo を呼んでいる。
Command と同様の操作をする必要はない。まあそれもそうか。
Qt のシグナルを一旦無効にする。
ポエム
Qt の SIGNAL/SLOT は強力なんだが、 Undo, Redo を実装しようとしてシグナル止めて無いとユーザ編集と Undo, Redo によるモデルの変更が分からなくて Undo スタックがぶっ壊れるなと思ったので一旦シグナルを止める方法を調べた。
やり方
bool oldState = checkBox->blockSignals(true); checkBox->setChecked(true); checkBox->blockSignals(oldState);
まんまや。
参考資料
スタオバさいつよ。
Qt の ui ファイルを cmake (CLion)から使う
ポエム
何度やっても QT_UIC_EXECUTABLE is missing
といいよる cmake に愛想を尽かさず頑張った。
Qt の *.ui ファイルを使う方法がわかったので記事を書く。Qt 記事少なすぎでしょみんなもっと記事書いて!英語記事ですらほとんどないじゃん。
症状
以下の様な CMakeLists
を 使うと、 QT_UIC_EXECUTABLE
無いんだけどwwwと cmake が激おこしてた。
# sources qt_wrap_ui(vsampler_corpus_editor view/ui_CorpusMetaInfoView.h view/ui_CorpusMetaInfoView.cpp vioew/CorpusMetaInfoView.ui) add_library(vsampler_corpus_editor ${files_you_need})
対処法
リファレンス見ると一見これで正しそうだしなんとかなりそうだがお前の qt_wrap_ui の使い方は間違っている。 結局ちゃんと設定ができていないのが問題だったらしい。
# sources qt5_wrap_ui(QT_CORPUS_EDITOR_UI_HEADERS hoge.ui foo.ui) add_library(vsampler_corpus_editor ${QT_CORPUS_EDITOR_UI_HEADERS} ${files_you_need})
こうしろってさ。んで cpp, c++ ファイルからこうせよってさ。
#include "ui_Hoge.h" #include "Hoge.h" hoge::Hoge(QWidget *parent) : QMainWindow(parent), ui(new Ui::Hoge) { ui->setupUi(this); } hoge::Hoge::~Hoge() { delete ui; }
cpp ファイルの方は Qt Creator に準じているだけ。これで *.ui
のヘッダファイルが生成されていい感じになる。
一週間かかった死にたい。
Qt を cmake から使う(CLion から使う)。
概要
Qt の開発というととりあえず QtCreator を使えばいいだろ、と思うのだが、最近 JetBrains から CLion という神ツールが発売された。
業務が IntelliJ だもので操作感が同じというのもあるんだが、 C++ でリファクタリングができるというさいつよツールなのでこれを使いたい。 しかしながら CLion は cmake 形式のプロジェクトしか読み込めない。
対処
Qt は cmake のビルドにも対応している。良い。(QtCreator から使うと恐ろしく扱いづらいが)
基本はこれにしたがって設定すれば良い。あとは作った cmake プロジェクトを CLion から読みこめるので Qt を利用したコードを CLion から利用できる。 SIGANAL/SLOT などの Qt の C++ 拡張部分も問題なく使えた。
システム変数の追加
CMAKE_PREFIX_PATH
に Qt のディレクトリを追加する。僕の環境だと C:\Qt\5.3\mingw482_32
だった。
これで cmake が Qt の位置を知ることができる。
cmake の記述
# Created by Hal@shurabaP project(sample) # configuration for Qt set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) # cmake options set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") # dependency find_package(Qt5Core) get_target_property(QtCore_location Qt5::Core LOCATION) find_package(Qt5Gui) get_target_property(QtCore_location Qt5::Gui LOCATION) find_package(Qt5Multimedia) get_target_property(QtCore_location Qt5::Multimedia LOCATION) find_package(Qt5Widgets) get_target_property(QtCore_location Qt5::Widgets LOCATION) # sources add_executable(sample main.cpp) target_link_libraries(sample LINK_PUBLIC Qt5::Core Qt5::Gui Qt5::Multimedia Qt5::Widgets)
cmake よく知らないから間違ってるかも。とりあえずビルドして実行できたので僕はこの設定を使っている。
C++ でエラー処理書いてたら死にそうになったので Scala の Try パクってみた。
ポエム
業務で Scala を書いてるせいか、趣味で C++ 書いていると C++ のエラー処理のヤバさにつらみが溜まるし発狂したい。こういうのほんとうに辛い。
/** * path のファイルに書かれた文字列を返します。 * エラーがあった場合は err に値が書き込まれます。 */ string load(const string &path, int *err);
異常系の処理が貧弱すぎてつらい。何が嫌かって、きっとこの後ファイルの内容をパーズするわけで、 こんな感じの関数が作られるに違いない。
/** * str を json に変換します。 * エラーがあった場合は err に値が書き込まれます。 */ json parse(const string &str, int *err);
実際に使われるのはきっとこんな感じになるに違いない。
void main() { int err; string str(load("hoge.txt", &err)); if(err != 0) { printf("File load error!!\n"); exit(-1); } json j(parse(str, &err)); switch(err) { case ... } }
もうこんなの絶対発狂したい。でも例外使って try catch のハンドリングも面倒くさい。
Scala の Try パクった
上の例だとこんな感じになる。
Try<string> load(const string &path); Try<json> parse(const string &str); void main() { Try<json> result(load("hoge.txt").flatMap([](const string&str) -> Try<json>{ return parse(str); })); if(result.isFailure()) { exit(-1); } ... }
面倒くさいけどまあ伝統的な形式よりは…
一応テストも書いたし動く模様だ。でもあんまり効率的じゃなくて重そうだな、とは思った。 あと、例外投げるところでメモリリーク発生してるよねこれ。まあいっか。
qHash 関数と名前空間
概要
名前空間内で定義したクラスの qHash 関数の定義で手ひどい罠を踏んだので書いておく。
罠
これはダメなんだってさ。
namespace hoge { class Hoge { public: int foo; } } bool operator == (const hoge::Hoge &left, const hoge::Hoge &right) { return left.foo == right.foo; } unsigned int qHash(const hoge::Hoge& h) { return qHash(h); }
こうすると行ける。
namespace hoge { class Hoge { public: int foo; } bool operator == (const Hoge &left, const Hoge &right) { return left.foo == right.foo; } unsigned int qHash(const Hoge& h) { return qHash(h); } }
そっか。
参考資料
[QTBUG-34912] QHash compile error when using a key that is a class in a namespace - Qt Bug Tracker
音源のフォーマット考えている
Corpus-+--CorpusMetaInfo-+-name 音源名(言語別) | +-version バージョン名 | +-iconPath アイコンファイル名 | +-samplePath サンプル波形のファイル名 | +-author 製作者(言語別) | +-web 公開ページ | +-license ライセンス文(言語別) | +-description 自由記述(言語別) +--PhonemeSet-+-phoneme[0] 音素片 -+-pronounce 発音 | +-label なんか識別子 | +-path ファイル名 | +-type 音素片種別 CVCとか | +-offset 左ブランク | +-length 音素長 | +-preutterance 位置補正 | +-loopBeginMs 左固定長 | +-koopEndMs 右固定長 | +-MusicalContext-+- noteNumber | +- brightness | +- velocity | +- tempo | +- duration +-phoneme[1] 音素片 -+-... | +-... ... ...
こういう構造を考えた。
追記:@maruLoop氏に指摘されてライセンス・バージョン追加