SDツールの土台作成 [Qt]
今回はSDカードツールの話にします。と言っても、このツールはあんまり必要じゃ無さげになってきたんですけどね・・。まあ、なんでもいいので取りかかっていきましょう。
*
まずは、プログラムを埋め込む枠を作るためにGUIから作成します。とりあえずは「ドラッグ&ドロップでSDカードのインポートフォルダにファイルをコピーする」と言うのをアプリの機能としたので、QTextEditのみを使って、そこへのドラッグ&ドロップで動作、動作状況はQTextEdit上にテキストで表示すると言う感じで対応しようと思います。・・って、だんだんやることの敷居が低くなってますね・・。
プロジェクト作成して、ウィンドウにQTextEditを貼り付けて、レイアウトをごにょごにょいじって、保存して、実行。
土台完成
ま、ここまでは、これまでやってきたことの焼き直しですね。カーソルが表示されていたので、readOnlyに設定したいのですが、NetWalker 上のQt Creatorでは、プロパティの編集ができないので、Qt Designer を起動して設定します。・・・、そう言えば、このソフト自体はMac向けを目指していますが、現状これの開発はNetWalker上で行ってます。NetWalkerだと、どこででも開発できるので・・。AndroidのSDKもARM向けが出ればいいんですけどね・・。
ま、それはさておき、「readOnly」のプロパティにチェックを入れて、Qt Creatorで再読み込みし、同プロパティを見てみましたが、反映されてません・・。でも、実行してみたら、カーソルは出てきませんでした。と言うことで、readOnlyにはなっている様です。う~ん、NetWalkerでは、Qt Creator上のDesignerの使用はやめましょうかね・・。
図に示すほどでもないですが、今、クラス構成は下記の様な感じにしようかと思っています。
単純
役割分担は下記の様にしましょう。
と言うことで、SdtoolMainと言うクラスの追加をしときます。ベースクラスは無しですね。
とまあ、同じようなことばかり繰り返してますが、つづく。
*
まずは、プログラムを埋め込む枠を作るためにGUIから作成します。とりあえずは「ドラッグ&ドロップでSDカードのインポートフォルダにファイルをコピーする」と言うのをアプリの機能としたので、QTextEditのみを使って、そこへのドラッグ&ドロップで動作、動作状況はQTextEdit上にテキストで表示すると言う感じで対応しようと思います。・・って、だんだんやることの敷居が低くなってますね・・。
プロジェクト作成して、ウィンドウにQTextEditを貼り付けて、レイアウトをごにょごにょいじって、保存して、実行。
土台完成
ま、ここまでは、これまでやってきたことの焼き直しですね。カーソルが表示されていたので、readOnlyに設定したいのですが、NetWalker 上のQt Creatorでは、プロパティの編集ができないので、Qt Designer を起動して設定します。・・・、そう言えば、このソフト自体はMac向けを目指していますが、現状これの開発はNetWalker上で行ってます。NetWalkerだと、どこででも開発できるので・・。AndroidのSDKもARM向けが出ればいいんですけどね・・。
ま、それはさておき、「readOnly」のプロパティにチェックを入れて、Qt Creatorで再読み込みし、同プロパティを見てみましたが、反映されてません・・。でも、実行してみたら、カーソルは出てきませんでした。と言うことで、readOnlyにはなっている様です。う~ん、NetWalkerでは、Qt Creator上のDesignerの使用はやめましょうかね・・。
図に示すほどでもないですが、今、クラス構成は下記の様な感じにしようかと思っています。
単純
役割分担は下記の様にしましょう。
MainWindowClass | メインウィンドウで、アプリの実行・終了の制御 |
QTextEdit | ファイル・ディレクトリのドロップ場所及び、状況の表示 |
SdtoolMain | ディレクトリのチェックとかファイルのコピーとか実行全般 |
と言うことで、SdtoolMainと言うクラスの追加をしときます。ベースクラスは無しですね。
とまあ、同じようなことばかり繰り返してますが、つづく。
BackupControlクラスのメンバ関数仕様検討 [Qt]
大体の機能分担を決めたので、それに従って実装して行きたいと思います。BackupControlは以前作成したので、BackupMainも作成しておきます。こちらは、QThreadの派生クラスとします。
*
まずは、BackupControlに以前検討したメソッドを追加しようと思いましたが、自作のクラスは自分でソースを編集する必要がありそうですね・・。クラスエディタとかがあればいいのに。
さて、コードを書こうと思いましたが、よく考えたら引数とかを全く決めてませんでした。そこから始めないといけませんね。
引数を参照渡しにするかポインタ渡しにするか悩みましたが、いくつかサイトを検索して、おおよそ下記の方針で使い分けることにしました。
getSourceDirs() で、クラスのオブジェクトを返す時にどうするか悩みました。値で返すのか、ポインタで返すのか・・。自分で作ったクラスを返す時は注意する必要がある(コピーコンストラクタを作る?)が、Qtで用意されているクラスならそのまま値返しができそうな雰囲気です。ちょっと不安なんですが、まあ、今回は試しに値返しにしてみることにしました。何か不具合が出たら、それはそれでいい勉強になるでしょう。今回、色々悩んでて、クラスに対する代入演算子や初期化演算子の動きがよくわかってい無かったことが判明しましたね・・。理解できたんかなぁ・・。
スレッド実行中の処理について、排他処理を行う必要がありますね・・。うまくいくんかな・・。やり方が間違っているか・・?
さて、以下は signal です。とりあえずは、QThreadが元から持っているsignalは出てくるとして、他に何かいりますかね?と思ったら、以前検討してました。全部いるかな・・?
今、思いついた関数仕様はこんなところでしょうか。実装する前に、ちゃんと動作を検討しとく必要がありますね・・。
つづく。
【参考】
・参照とポインタ - Gulf of St.Lawrence
・ポインタと参照の使い分け - thinkin’ in the brain
・オブジェクトを返す関数 - C++ Builder / Turbo C++ 質問の木
・C++編(言語解説) 第16章 コピーコンストラクタ
・C++ クラス設計に関するノート - オブジェクトの広場
・C++ で関数から object をどう返すか - KBDAHOLIC - やぬすさんとこ
・C++ クラス設計に関するノート
・コピーコンストラクタ - C++入門
*
まずは、BackupControlに以前検討したメソッドを追加しようと思いましたが、自作のクラスは自分でソースを編集する必要がありそうですね・・。クラスエディタとかがあればいいのに。
さて、コードを書こうと思いましたが、よく考えたら引数とかを全く決めてませんでした。そこから始めないといけませんね。
addSourceDir() | |||
引数 | QString &dir | 転送元ディレクトリ | |
戻り値 | int | ディレクトリ登録の成否。下記の値のいずれかを返す。 | |
AddSrcSuccess | 追加成功 | ||
AddSrcDuplicate | 追加済みディレクトリ | ||
AddSrcBackupProceeded | バックアップ中 | ||
AddSrcFail | 追加失敗 | ||
処理 | 引数で与えられたディレクトリを転送元ディレクトリとして追加する。追加失敗は、存在しないディレクトリ名の指定を想定。「AddSrcBackupProceeded」はUI的に発生しない様にする予定。 |
delSourceDir() | |||
引数 | QString &dir | 削除対象ディレクトリ | |
戻り値 | int | ディレクトリ削除の成否。下記のいずれかを返す。 | |
DelSrcSuccess | 削除成功 | ||
DelSrcBackupProceeded | バックアップ処理中 | ||
DelSrcFail | 削除失敗 | ||
処理 | 引数で指定されたディレクトリを転送元ディレクトリから削除する。削除失敗は、指定されたディレクトリが転送元として追加されていない場合を想定。「DelSrcBackupProceeded」はUI的に発生させないようにする予定。 |
backup() | |||
引数 | なし | ||
戻り値 | int | バックアップ開始結果。下記のいずれかを返す。 | |
BackupStartSuccess | バックアップ開始成功 | ||
BackupStartNotSetSrc | バックアップ元ディレクトリ未設定 | ||
BackupStartNotSetDest | バックアップ先ディレクトリ未設定 | ||
BackupStartBackupProceeded | バックアップ中 | ||
BackupStartFail | バックアップ開始失敗 | ||
処理 | BackupMainに転送元ディレクトリリストと転送先ディレクトリを渡し、BackupMainの処理を開始する。バックアップ開始失敗はThread開始失敗を想定。「BackupStartBackupProceeded」はUI的に発生させないようにする予定。 |
setDestinationDir() | |||
引数 | QString &dir | 転送先ディレクトリ | |
戻り値 | int | 転送先ディレクトリ設定結果。下記のいずれかを返す | |
SetDestSuccess | 転送先ディレクトリ設定成功 | ||
SetDestDuplicate | 設定済み転送先ディレクトリ | ||
SetDestDuplicate | 設定済み転送先ディレクトリ | ||
SetDestBackupProceeded | バックアップ中 | ||
SetDestFail | 転送先ディレクトリ設定失敗 | ||
処理 | 引数で指定されたフォルダを転送先フォルダとして設定する。設定失敗は指定されたフォルダが存在しない場合を想定。「SetDestBackupProceeded」はUI的に発生させない予定。 |
getSourceDirs() | ||
引数 | なし | |
戻り値 | QStringList | 設定されている転送元ディレクトリのリスト |
処理 | 設定されている転送元ディレクトリのリストをまとめて返す。 |
getDestinationDir() | ||
引数 | なし | |
戻り値 | QString | 設定されている転送先ディレクトリ |
処理 | 設定されている転送先ディレクトリを返す。何も設定されていない場合は、空文字列を返す(isEmpty()でtrueになる)。 |
stop() | |||
引数 | なし | ||
戻り値 | int | バックアップ停止結果。下記を返す。 | |
BackupStopSuccess | バックアップ停止成功 | ||
BackupStopNotProceed | バックアップ処理中でない | ||
BackupStopFail | バックアップ停止失敗 | ||
処理 | 実行中のバックアップ処理を停止させる。処理停止のフラグでも立てるか?そうすると、バックアップの実行中にフラグチェックの処理がいりますね。QThread::terminate()は使いません。 |
引数を参照渡しにするかポインタ渡しにするか悩みましたが、いくつかサイトを検索して、おおよそ下記の方針で使い分けることにしました。
- ポインタ渡し - 引数を変更する可能性がある場合、NULLの引数を許容する場合
- 参照渡し - 引数を変更しない場合、NULLの引数を許容しない場合
getSourceDirs() で、クラスのオブジェクトを返す時にどうするか悩みました。値で返すのか、ポインタで返すのか・・。自分で作ったクラスを返す時は注意する必要がある(コピーコンストラクタを作る?)が、Qtで用意されているクラスならそのまま値返しができそうな雰囲気です。ちょっと不安なんですが、まあ、今回は試しに値返しにしてみることにしました。何か不具合が出たら、それはそれでいい勉強になるでしょう。今回、色々悩んでて、クラスに対する代入演算子や初期化演算子の動きがよくわかってい無かったことが判明しましたね・・。理解できたんかなぁ・・。
スレッド実行中の処理について、排他処理を行う必要がありますね・・。うまくいくんかな・・。やり方が間違っているか・・?
さて、以下は signal です。とりあえずは、QThreadが元から持っているsignalは出てくるとして、他に何かいりますかね?と思ったら、以前検討してました。全部いるかな・・?
finished() | ||
引数 | なし | |
戻り値 | なし | |
処理 | スレッドの実行完了時に発生。コピー処理終了のsignalとして利用可能かな。 |
started() | ||
引数 | なし | |
戻り値 | なし | |
処理 | スレッドの実行開始時に発生。バックアップ開始のシグナルとして利用可能かな。 |
terminated() | ||
引数 | なし | |
戻り値 | なし | |
処理 | スレッドがterminateされたときに発生。QThread::terminate()を使う予定はないので、これは発生しない想定。なので、利用しない。 |
stopped() | ||
引数 | なし | |
戻り値 | なし | |
処理 | バックアップ停止完了時に発生。 |
startBackupFile() | ||
引数 | QString &dir | バックアップ元ファイルフルパス |
戻り値 | なし | |
処理 | 1ファイルのバックアップ開始時に発生。引数はバックアップファイルのフルパス。 |
finishBackupFile() | ||
引数 | QString &dir | バックアップ元ファイルフルパス |
戻り値 | なし | |
処理 | 1ファイルのバックアップ完了時に発生。引数はバックアップ元ファイルのフルパス。 |
今、思いついた関数仕様はこんなところでしょうか。実装する前に、ちゃんと動作を検討しとく必要がありますね・・。
つづく。
【参考】
・参照とポインタ - Gulf of St.Lawrence
・ポインタと参照の使い分け - thinkin’ in the brain
・オブジェクトを返す関数 - C++ Builder / Turbo C++ 質問の木
・C++編(言語解説) 第16章 コピーコンストラクタ
・C++ クラス設計に関するノート - オブジェクトの広場
・C++ で関数から object をどう返すか - KBDAHOLIC - やぬすさんとこ
・C++ クラス設計に関するノート
・コピーコンストラクタ - C++入門
クラス間の機能分担とQtのスレッド [Qt]
さて、今週はQtの話に戻ります。前回、バックアップ処理の実現に何らかの並行処理が必要なことに気付きました。って、最初から気づけよと言う感じですかね・・。手動で停止することが頭に無かったのでした。
「並行処理」でぱっと思いついたのが「スレッド」ですが、Qtにはどのような並行処理が用意されているのでしょうか・・?と思って、リファレンスを眺めていたら、スレッドのページがありました。ここを読んで行きましょう。
*
単純にスレッドを実行するには、下記の通り行えばいい様です。
クラス図にすれば、こんな感じでしょうか?ファイルのバックアップ処理を行うクラスを「BackupMain」としました。
バックアップ処理をスレッド化する際のクラス図
どういう機能のクラスにすればいいですかねぇ・・。バックアップ処理の分担分けとしては、下記の通りにしたい気がします。
実装イメージとしては、BackupMain内にバックアップ元、バックアップ先(今回の場合、バックアップ元一つ一つに持たせる意味はありませんが・・)のペアを保存するキューを持たせ、BackupControlからそこにファイルのパスを追加、BackupMainでパスを取り出してコピーを実行としたいと思っています。
・・・とここまで考えていましたが、も一つスレッドが必要かも・・・。バックアップ元のファイルの抽出をBackupControlで行っていると、停止ボタンのイベントが拾えない気がします。と言うか、ファイルパスの抽出と実行を同じスレッドですればいいか・・。
機能分担を考えなおすとこんな感じでしょうか。
ま、ほぼ一緒ですね。バックアップ元のディレクトリとバックアップ先ディレクトリをまとめてBackupMainに渡して、後は勝手にやってくれと言う感じにしましょう。
先程のキューの排他処理が必要かと思ってましたが、この構成にすれば、それも不要ですね。
と言う感じで、実装して行きましょう。
【参考】
・Thread Support in Qt - Qt 4.5.3
「並行処理」でぱっと思いついたのが「スレッド」ですが、Qtにはどのような並行処理が用意されているのでしょうか・・?と思って、リファレンスを眺めていたら、スレッドのページがありました。ここを読んで行きましょう。
*
単純にスレッドを実行するには、下記の通り行えばいい様です。
- QThreadのサブクラスを作成。
- サブクラス内で、QThread::run()に実行させたい処理を記述。
- QThread::start()でスレッドを実行。
クラス図にすれば、こんな感じでしょうか?ファイルのバックアップ処理を行うクラスを「BackupMain」としました。
バックアップ処理をスレッド化する際のクラス図
どういう機能のクラスにすればいいですかねぇ・・。バックアップ処理の分担分けとしては、下記の通りにしたい気がします。
BackupControl | バックアップ元ファイルとバックアップ先ディレクトリを選定し、BackupMainに通知。 BackupMainの開始と停止制御。 |
BackupMain | BackupControlから指定された情報を元に順次バックアップを実行。 |
実装イメージとしては、BackupMain内にバックアップ元、バックアップ先(今回の場合、バックアップ元一つ一つに持たせる意味はありませんが・・)のペアを保存するキューを持たせ、BackupControlからそこにファイルのパスを追加、BackupMainでパスを取り出してコピーを実行としたいと思っています。
・・・とここまで考えていましたが、も一つスレッドが必要かも・・・。バックアップ元のファイルの抽出をBackupControlで行っていると、停止ボタンのイベントが拾えない気がします。と言うか、ファイルパスの抽出と実行を同じスレッドですればいいか・・。
機能分担を考えなおすとこんな感じでしょうか。
BackupControl | バックアップ元ディレクトリとバックアップ先ディレクトリをBackupMainに通知。 BackupMainの開始と停止制御。 |
BackupMain | BackupControlから指定された情報を元に対象ディレクトリを選定し、順次バックアップを実行。 |
ま、ほぼ一緒ですね。バックアップ元のディレクトリとバックアップ先ディレクトリをまとめてBackupMainに渡して、後は勝手にやってくれと言う感じにしましょう。
先程のキューの排他処理が必要かと思ってましたが、この構成にすれば、それも不要ですね。
と言う感じで、実装して行きましょう。
【参考】
・Thread Support in Qt - Qt 4.5.3
バックアップの処理クラスを考える [Qt]
先日、「mainwindow.ui」に以前追加したslotが残っている件を気にしていましたが、それを削除できないかと思っていたところ、できました。
Objectペインの「MainWindowClass」を右クリックし、「change signals/slots」を選択します。
こっちにはありました
「Slots」の欄に「addButtonClicked()」が残っていたので、これを選択して「-」ボタンを押して削除します。
ここに残ってます
結果を保存して、「mainwindow.ui」をテキストエディタで見たところ、該当の箇所の記述が消えていました。
消えた!
*
以上、すっきりしたところで、実装を進めようと思いますが、進める前にクラス構成を考えたいと思います。今まで、MainWindowクラスに色々貼り付けていましたが、ここは単なるUIとして、バックアップ制御を受け持つクラスを別に用意したいと思います。クラス図を書くとこんな感じでしょうか・・。
描くほどでもないですが・・
アプリケーション自体はMainWindowクラス主体で動くので、そのエンジンとなる「BackupControl」と言うクラスを作成します。ベースクラスは無しとします。
MainWindowクラスとこのクラス(BackupControl)の間でBackupControlクラス側に必要そうなメソッドは下記のような感じでしょうか。
最後のメソッドで思いつきましたが、そう言えば、バックアップ動作の中止のボタンが必要ですね・・。
また、経過出力のためにsignalを出してもらう必要がありそうです。signalでいいんですかね・・。
ここまで書いて気付きましたが、これって、バックアップ処理は並列処理で行わないと途中で停止ができない気がしますね・・。Qtのスレッドの仕組みを見なければ・・。
つづく。
Objectペインの「MainWindowClass」を右クリックし、「change signals/slots」を選択します。
こっちにはありました
「Slots」の欄に「addButtonClicked()」が残っていたので、これを選択して「-」ボタンを押して削除します。
ここに残ってます
結果を保存して、「mainwindow.ui」をテキストエディタで見たところ、該当の箇所の記述が消えていました。
消えた!
*
以上、すっきりしたところで、実装を進めようと思いますが、進める前にクラス構成を考えたいと思います。今まで、MainWindowクラスに色々貼り付けていましたが、ここは単なるUIとして、バックアップ制御を受け持つクラスを別に用意したいと思います。クラス図を書くとこんな感じでしょうか・・。
描くほどでもないですが・・
アプリケーション自体はMainWindowクラス主体で動くので、そのエンジンとなる「BackupControl」と言うクラスを作成します。ベースクラスは無しとします。
MainWindowクラスとこのクラス(BackupControl)の間でBackupControlクラス側に必要そうなメソッドは下記のような感じでしょうか。
addSourceDir() | バックアップ元ディレクトリの追加 |
delSourceDir() | バックアップ元ディレクトリの削除 |
backup() | バックアップ開始 |
setDestinationDir() | バックアップ先ディレクトリの設定 |
getSourceDirs() | 設定されているバックアップ元ディレクトリの取得 |
getDiestinationDir() | 設定されているバックアップ先ディレクトリの取得 |
stop() | バックアップの停止 |
最後のメソッドで思いつきましたが、そう言えば、バックアップ動作の中止のボタンが必要ですね・・。
また、経過出力のためにsignalを出してもらう必要がありそうです。signalでいいんですかね・・。
startCopyFile() | 1ファイルコピー開始のsignal(コピー元) |
finishCopyFile() | 1ファイルコピー終了のsignal(コピー元) |
startCopy() | コピー処理開始のsignal |
finishCopy() | コピー処理終了のsignal |
stopCopy() | コピー処理停止のsignal |
ここまで書いて気付きましたが、これって、バックアップ処理は並列処理で行わないと途中で停止ができない気がしますね・・。Qtのスレッドの仕組みを見なければ・・。
つづく。
タイトルバーの文字化けを直す [Qt]
さて、ファイル選択ダイアログのタイトルバーの文字が化けてしまう件ですが、まずは、先日調べた以下のフレーズを試してみようと思います。今のところ翻訳データは無いので、main()に下記を追加してみます。
ソースはこんな感じです。
これでうまくいくといいですけどねぇ・・
これで実行してみます。・・・コンパイルエラーが出ました・・。
ヘッダをインクルードするのを忘れてました。QTextCodecのページを見たところ、「#include <QTextCodec>」だとのこと。
追加して実行したところ・・。
文字化けが直りました
正常に表示できました。と言うことで、簡単ですが、今回はこれで。
QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8")); |
ソースはこんな感じです。
#include <QtGui/QApplication> #include "mainwindow.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8")); w.show(); return a.exec(); } |
これで実行してみます。・・・コンパイルエラーが出ました・・。
ヘッダをインクルードするのを忘れてました。QTextCodecのページを見たところ、「#include <QTextCodec>」だとのこと。
追加して実行したところ・・。
文字化けが直りました
正常に表示できました。と言うことで、簡単ですが、今回はこれで。
ディレクトリ選択ダイアログの表示 [Qt]
ウィンドウタイトルを変えるだけで、えらいとまどってしまいましたが、気を取り直して、とりあえず、各ボタンのスロットを作っていきます。まずは、関数の枠から・・。
ボタンを右クリックし、「Go to slot...」を実行して行きます。
追加は容易になりました
残り三つのボタンに対応するslotを作成して、ヘッダファイルを見てみたら、インデントがおかしいです・・。カーソル位置がおかしかったのか・・?
これは手で直します
ソースを見る限り、追加したslotは「private slots:」ラベルの下に追加されていくみたいですね。後から追加した方が上の方に追加されています。
ちなみに、関数の枠もちゃんとできています。
こっちは作成した順に追加されています
ログを仕込んで、実行したところ、全てのボタンが反応しました。
ログで確認
では、とりあえずQFileDIalogを「追加」ボタンから呼び出してみます。簡単に使いたいならstatic関数を呼べばいいとのこと。下記の関数が用意されています。
コピー元の追加は複数指定できた方が便利ですし、できたらディレクトリだけでなくファイル名も指定できると自由度が増す気がします。と言うことで、QFileDialog::getOpenFileNamesを使ってみたいのですが、これがディレクトリを指定できるのかどうかが不明です。まあ、試してみましょう。
引数は下記のものが用意されています。
とりあえず、このように実装してみました。
うまくいくか?
実行して、ファイルダイアログを起動したところ、このような画面に・・。タイトルが化けてます・・。
タイトル部分が文字化け・・
ディレクトリを選択して、「開く」を押したところ、そのディレクトリ内に入ってしまいました・・。ファイルを選択して「開く」を押したらうまくいった様です・・。このダイアログではディレクトリは選択できませんね・・。
と言うことで、QFileDialog::getOpenFileNamesでは、ディレクトリの選択は出来ませんね・・。
仕方がないので、QFileDialog::getExistingDirectoryを利用します。
うまくいくか?
これで、1つづつですが、ディレクトリの選択は出来るようになりました。が、相変わらず、キャプションの文字化けが残っています・・。
文字化けを直したい
次回はこれを調べましょう。つづく。
ボタンを右クリックし、「Go to slot...」を実行して行きます。
追加は容易になりました
残り三つのボタンに対応するslotを作成して、ヘッダファイルを見てみたら、インデントがおかしいです・・。カーソル位置がおかしかったのか・・?
これは手で直します
ソースを見る限り、追加したslotは「private slots:」ラベルの下に追加されていくみたいですね。後から追加した方が上の方に追加されています。
ちなみに、関数の枠もちゃんとできています。
こっちは作成した順に追加されています
ログを仕込んで、実行したところ、全てのボタンが反応しました。
ログで確認
では、とりあえずQFileDIalogを「追加」ボタンから呼び出してみます。簡単に使いたいならstatic関数を呼べばいいとのこと。下記の関数が用意されています。
QFileDialog::getExistingDirectory | 存在するディレクトリの指定に使用。 |
QFileDialog::getOpenFileName | 存在するファイルを一つだけ指定するのに使用。 |
QFileDialog::getOpenFileNames | 存在する一つ以上のファイルを指定するのに使用。 |
QFileDialog::getSaveFileName | 一つのファイル名を指定するのに使用。ファイルは存在していても無くてもどちらでもいい。 |
コピー元の追加は複数指定できた方が便利ですし、できたらディレクトリだけでなくファイル名も指定できると自由度が増す気がします。と言うことで、QFileDialog::getOpenFileNamesを使ってみたいのですが、これがディレクトリを指定できるのかどうかが不明です。まあ、試してみましょう。
引数は下記のものが用意されています。
QWidget *parent = 0 | 親ウィジェット。これを設定するとモーダルダイアログになるとのこと。親ウィジェットの中央に表示されるとのこと。thisでいいと思います。 |
const QString & caption = QString() | ダイアログのタイトルバーに表示される文字列ですね。設定しない場合はデフォルトの文字列が使われるとのこと。 |
const QString & dir = QString() | ワークディレクトリ。ファイル名を含む場合は、そのファイルが選択された状態になる。 |
const QString & filter = QString() | フィルター。複数のフィルターを指定する時は、「;;」で区切るとのこと。書式が不明・・。 |
QString * selectedFilter = 0 | 選択されているフィルター。 |
Options options = 0 |
とりあえず、このように実装してみました。
void MainWindow::on_addButton_clicked() { qDebug() << "on_addButton_clicked()"; QStringList *srcList = new QStringList(); *srcList = QFileDialog::getOpenFileNames(this, tr("バックアップ元の選択")); qDebug() << *srcList; delete srcList; } |
実行して、ファイルダイアログを起動したところ、このような画面に・・。タイトルが化けてます・・。
タイトル部分が文字化け・・
ディレクトリを選択して、「開く」を押したところ、そのディレクトリ内に入ってしまいました・・。ファイルを選択して「開く」を押したらうまくいった様です・・。このダイアログではディレクトリは選択できませんね・・。
と言うことで、QFileDialog::getOpenFileNamesでは、ディレクトリの選択は出来ませんね・・。
仕方がないので、QFileDialog::getExistingDirectoryを利用します。
void MainWindow::on_addButton_clicked() { qDebug() << "on_addButton_clicked()"; QStringList *srcDir = new QStringList(); *srcList = QFileDialog::getExistingDirectory(this, tr("バックアップ元の選択")); qDebug() << *srcDir; delete srcDir; } |
これで、1つづつですが、ディレクトリの選択は出来るようになりました。が、相変わらず、キャプションの文字化けが残っています・・。
文字化けを直したい
次回はこれを調べましょう。つづく。
Qt Designer上での各部品のプロパティの変更 [Qt]
前回の記事で、Qt Designer上でのslotの追加の仕方が概ねわかりました。slotの作成に各部品の名前が関わってくることが判明したので、まず、各部品に名前を付けることにしました。つけた名前は、下記の通りです。
各部品の対応は下記の通りです。
名前を付けるのは難しいですね
そう言えば、ウィンドウのタイトルも変えておきました。一応、英字にしときましたが・・。
とさらっと書きましたが、変え方がなかなかわかりませんでした・・。Object Inspector(右上)で「MainWindowClass」を選択して、Property Editor(右下と言うか、右中央?)で「windowTitle」と言うプロパティを変更しようとしましたが、編集できる状態になりません・・・。説明を見る限り、ここのValueの欄をクリックすれば、変更できそうな感じなのですが、いくらクリックしても編集状態に変わりませんでした・・。
かなり、いらいら状態・・
調べても良くわからないので、Qt Creator上で変更するのはあきらめて、同時にインストールされていたQt Designerを別途起動して変更を試みたところ、あっさりできました・・・・。
ここまで来るのに苦労しました・・。
う~ん、Qt Creator に組み込まれているものと、同時にインストールされた Qt Desginerではバージョンが違うんですかね・・。何となく、最新のものでは大丈夫で、今のところNetWalker上だけの症状の様な気もしますが、ご参考まで。
つづく。
部品名 | オブジェクト名 | 概要 | |
1 | バックアップ対象リスト表示領域 | srcList | バックアップ対象のディレクトリを列挙する。 |
2 | 追加ボタン | addButton | バックアップ対象ディレクトリの追加のためのボタン。 |
3 | 削除ボタン | delButton | バックアップ対象ディレクトリの削除のためのボタン。 |
4 | バックアップボタン | startButton | バックアップ開始のボタン。 |
5 | バックアップ先表示領域 | destDir | バックアップ先のディレクトリを表示。 |
6 | 選択ボタン | destButton | バックアップ先のディレクトリを選択するためのボタン。 |
7 | 進捗状況表示領域 | progressView | 進捗状況を表示する。 |
各部品の対応は下記の通りです。
名前を付けるのは難しいですね
そう言えば、ウィンドウのタイトルも変えておきました。一応、英字にしときましたが・・。
とさらっと書きましたが、変え方がなかなかわかりませんでした・・。Object Inspector(右上)で「MainWindowClass」を選択して、Property Editor(右下と言うか、右中央?)で「windowTitle」と言うプロパティを変更しようとしましたが、編集できる状態になりません・・・。説明を見る限り、ここのValueの欄をクリックすれば、変更できそうな感じなのですが、いくらクリックしても編集状態に変わりませんでした・・。
かなり、いらいら状態・・
調べても良くわからないので、Qt Creator上で変更するのはあきらめて、同時にインストールされていたQt Designerを別途起動して変更を試みたところ、あっさりできました・・・・。
ここまで来るのに苦労しました・・。
う~ん、Qt Creator に組み込まれているものと、同時にインストールされた Qt Desginerではバージョンが違うんですかね・・。何となく、最新のものでは大丈夫で、今のところNetWalker上だけの症状の様な気もしますが、ご参考まで。
つづく。
Qt Designerで追加したslotの削除とsignal/slotのconnect [Qt]
さて、slotを追加できたのはいいんですが、どうやって削除すればいいんでしょうか・・? いろいろクリックしていても、削除の方法が出てこなさげです。
今回、追加ボタンの名前を変えたいだけなので、試しにボタンの名前を「pushButton」から「addButton」に変えてみました。そして、右クリック→「Go to slot」したところ、また新しいslotが追加されました・・。できたスロットは「on_addButton_clicked」。名前は固定ですかね・・? もしかしたら、これで古い方は削除すればいいだけなのかもしれません。
よくわかりませんが、これで実行してみましたところ、新しく作成したslotが呼ばれていました・・。じゃあ、削除は手動ですかね・・。と言うことで、前々回と前回追加した項目は手動で削除することにします。
で、実行。で、問題無く動作しました。と、それはいいんですが、ソースを見てもsignalとslotをconnectしている箇所が見当たりません・・。何故つながってるんでしょうか・・?
UI関連のファイルに定義されているのかなと思い、「mainwindow.ui」をテキストエディタで見てみたところ、ソースの末尾に「<slots>」と言う項目が追加されていました。
スロットの記述はありました
これ、最初に作成したslotですね・・。これを消すにはどうすればいいんでしょう・・・?
目的のものが無かったので、次に「ui_mainwindow.h」を覗いてみたところ、「Ui_MainWindowClass」と言うクラスの「setupUi()」と言う関数の中に「QMetaObject::connectSlotsByName(MainWindowClass);」との記載があります。これがそうなんですかねぇ・・。
これ?
と言うことで、「QMetaObject」の説明を見てみます。そこの「connectSlotsByName()」関数の説明を見てみると、全ての子オブジェクトから「void on_<object name>_<signal name>(<signal parameters>);」と言う形のslotを探して接続するとのこと。と言うことで、Qt Designerを使う時はsignalが自動で作成されるslotに自動でconnectされる様です。・・・結構、やっつけで作った感じがしないでもないですね。
・・・オブジェクトの名前を変えた時に追随してくれるといいんですが、それは無さげですね・・。「リファクタリング」っていうメニューでも無いんかな・・?
以上で、Qt Designerでのslotの追加と接続の方法がわかったので、次回より各オブジェクト名を変更して、追加して行くことにします。
つづく。
今回、追加ボタンの名前を変えたいだけなので、試しにボタンの名前を「pushButton」から「addButton」に変えてみました。そして、右クリック→「Go to slot」したところ、また新しいslotが追加されました・・。できたスロットは「on_addButton_clicked」。名前は固定ですかね・・? もしかしたら、これで古い方は削除すればいいだけなのかもしれません。
よくわかりませんが、これで実行してみましたところ、新しく作成したslotが呼ばれていました・・。じゃあ、削除は手動ですかね・・。と言うことで、前々回と前回追加した項目は手動で削除することにします。
で、実行。で、問題無く動作しました。と、それはいいんですが、ソースを見てもsignalとslotをconnectしている箇所が見当たりません・・。何故つながってるんでしょうか・・?
UI関連のファイルに定義されているのかなと思い、「mainwindow.ui」をテキストエディタで見てみたところ、ソースの末尾に「<slots>」と言う項目が追加されていました。
スロットの記述はありました
これ、最初に作成したslotですね・・。これを消すにはどうすればいいんでしょう・・・?
目的のものが無かったので、次に「ui_mainwindow.h」を覗いてみたところ、「Ui_MainWindowClass」と言うクラスの「setupUi()」と言う関数の中に「QMetaObject::connectSlotsByName(MainWindowClass);」との記載があります。これがそうなんですかねぇ・・。
これ?
と言うことで、「QMetaObject」の説明を見てみます。そこの「connectSlotsByName()」関数の説明を見てみると、全ての子オブジェクトから「void on_<object name>_<signal name>(<signal parameters>);」と言う形のslotを探して接続するとのこと。と言うことで、Qt Designerを使う時はsignalが自動で作成されるslotに自動でconnectされる様です。・・・結構、やっつけで作った感じがしないでもないですね。
・・・オブジェクトの名前を変えた時に追随してくれるといいんですが、それは無さげですね・・。「リファクタリング」っていうメニューでも無いんかな・・?
以上で、Qt Designerでのslotの追加と接続の方法がわかったので、次回より各オブジェクト名を変更して、追加して行くことにします。
つづく。
Qt Designerでのslotの追加 [Qt]
前の記事で疑問を提示しましたが、他に解決法もよくわからないので、釈然としないながらも手動で関数を追加することにします。
「mainwindow.h」に定義されているMainWindowClassのprivate区画に「void addButtonClicked();」を追加し、「mainwindow.cpp」に、関数の実装を記載します。
宣言を追加
とりあえず、デバグ出力
これで、実行してみます。
エラーが出ましたね。うまくconnectされていない様です。
うまくいかんねぇ・・
「Object::connect: No such slot MainWindow::addButtonClicked() in ui_window.h:135」と言うエラーが出ています。以前の記事を読み返したら、slotの追加自体を完全に手動に切り替えてますね。今回は、スロットの作成と接続はQt Designerでやっています。関係だけQt Designerで指定して、スロットの実体は手動で書くのかと思いましたが、根本的にそれは違うのでしょうか・・?
*
色々見てみましたがよくわからなかったので、当てずっぽうで、Edit widgetsモードでボタンを右クリックして、「Go to slot」を選択したところ、なにやら自動でスロットが作成されました・・。
名前も自動で付けられました(mainwindow.cpp)
ん~、こちらが正しい方法なのでしょうか?と言うことは、前回つなげた線は意味が無くなりますねぇ・・。と言うか、先にこちらを作ってから線をつなげるべきなんでしょうか・・?
slotの名前が気に入らないんですが、とりあえず、これで実行してみたところ・・、ちゃんと反応しました。ちなみに、「mainwindow.h」にもslotが追加されていました。
プロトタイプも自動作成(mainwindow.h)
なるほど、slotはこれで追加をすればいいんですね。でも、接続先を変えたい時はどうすればいいんでしょうか・・? ま、それはそれとして、名前の変更もしたいので、一旦これまで追加して行ったものを削除することにします。
*
と思いましたが、ボタンを右クリックして「Change signals/slots」を選択しようと思ったら、そのメニューが無くなっています・・。
なんで・・?
追加したslotの削除方法は無いんでしょうか・・・?
つづく。
【参考】
・Qt Designer's Signals and Slots Editing Mode
「mainwindow.h」に定義されているMainWindowClassのprivate区画に「void addButtonClicked();」を追加し、「mainwindow.cpp」に、関数の実装を記載します。
宣言を追加
とりあえず、デバグ出力
これで、実行してみます。
エラーが出ましたね。うまくconnectされていない様です。
うまくいかんねぇ・・
「Object::connect: No such slot MainWindow::addButtonClicked() in ui_window.h:135」と言うエラーが出ています。以前の記事を読み返したら、slotの追加自体を完全に手動に切り替えてますね。今回は、スロットの作成と接続はQt Designerでやっています。関係だけQt Designerで指定して、スロットの実体は手動で書くのかと思いましたが、根本的にそれは違うのでしょうか・・?
*
色々見てみましたがよくわからなかったので、当てずっぽうで、Edit widgetsモードでボタンを右クリックして、「Go to slot」を選択したところ、なにやら自動でスロットが作成されました・・。
名前も自動で付けられました(mainwindow.cpp)
ん~、こちらが正しい方法なのでしょうか?と言うことは、前回つなげた線は意味が無くなりますねぇ・・。と言うか、先にこちらを作ってから線をつなげるべきなんでしょうか・・?
slotの名前が気に入らないんですが、とりあえず、これで実行してみたところ・・、ちゃんと反応しました。ちなみに、「mainwindow.h」にもslotが追加されていました。
プロトタイプも自動作成(mainwindow.h)
なるほど、slotはこれで追加をすればいいんですね。でも、接続先を変えたい時はどうすればいいんでしょうか・・? ま、それはそれとして、名前の変更もしたいので、一旦これまで追加して行ったものを削除することにします。
*
と思いましたが、ボタンを右クリックして「Change signals/slots」を選択しようと思ったら、そのメニューが無くなっています・・。
なんで・・?
追加したslotの削除方法は無いんでしょうか・・・?
つづく。
【参考】
・Qt Designer's Signals and Slots Editing Mode
ボタンのイベントを取得したい [Qt]
前回、国際化対応について調べましたが、ボタンに書かれている文字列をQt Designer上で直接入力したところ、ソース上には現れませんね・・。ソースの文字列をtr()で囲おうと思ったんですが、これの国際化対応はまた別の処理が必要な気がしてきました・・。ちなみに文字列は.uiファイルに入っています。もう面倒なので、後回しにしますか・・。
*
ボタンの文字列を変更して、実行したものがこちらです。最初に書いた図とは、ボタン内の表記を変えていますが、まあ、とりあえずこちらの様な感じで行こうと思います。
画面上にもうちょっと説明が必要な気がしますね
さて、まずは「追加」及び「選択」を押したときにディレクトリ選択のダイアログが出る様にしたいと思います。と思ったところで、どこに処理を追加しようかなと悩んでしまいました。いきなり、レイアウトから始めたので、現状はMainWindowクラスしか存在していません。そういや、ボタンに動作を追加するのってどうでしたっけ・・?
その辺の話は、こちらに書かれている様です。って、以前見た様な見てない様な・・。「Edit」→「Edit signals/slots」でsignal/slotの編集モードに入ります。
モードの変更が必要なのね
まずは、「追加」のボタンからフォームにsignalを送って見ることにします。追加のボタンからぐいぃ〜っとドラッグすると、電気回路図のグランドみたいなマークが出てきて、離すと次のダイアログが出てきました。
clicked()につながるslotを追加?
signal(pushButton)側に「clicked()」と「clicked(bool)」がありますが、前者は単に後者の引数がfalseに設定されているものの様です。この引数は、ボタンが「checkable」の場合に意味があるようです。今回はいらないので、前者でいいですね。
MainWindowClass(QMainWindow)側にslotが何も出ていないので、「Edit」ボタンをクリックして、MainWindowクラスにslotを追加してみましょう。
これで関数を追加ですかね
「Slots」側の「+」ボタンを押して、slot関数を追加します。
ま、名前は普通に
追加されたslotを選択して、「OK」を押します。
OK
左下の「Show signals and slots inherited from QWidget」と言うのが気になりますね・・。試しに、チェックを入れたら、デフォルトで用意されているsignal/slotが全部表示されました。あ~、そう言うことね・・。
関係無かった・・
これは、今はどうでもいいことでした。気を取り直して「OK」をクリック。すると、フォーム上に接続の様子が出てきました。画面が狭いので、見にくいですね・・。
見にくい・・
さて、とりあえず、ここで保存をして、追加したslotである「addButtonClicked()」を編集したいと思います。と思って、ソースを見てみましたが、特に関数は追加されていません・・。どうすりゃええの・・?昔の記事では、手動で追加してるみたいですね。
んんん~。ほんとにそれが正解なのか・・? つづく。
*
ボタンの文字列を変更して、実行したものがこちらです。最初に書いた図とは、ボタン内の表記を変えていますが、まあ、とりあえずこちらの様な感じで行こうと思います。
画面上にもうちょっと説明が必要な気がしますね
さて、まずは「追加」及び「選択」を押したときにディレクトリ選択のダイアログが出る様にしたいと思います。と思ったところで、どこに処理を追加しようかなと悩んでしまいました。いきなり、レイアウトから始めたので、現状はMainWindowクラスしか存在していません。そういや、ボタンに動作を追加するのってどうでしたっけ・・?
その辺の話は、こちらに書かれている様です。って、以前見た様な見てない様な・・。「Edit」→「Edit signals/slots」でsignal/slotの編集モードに入ります。
モードの変更が必要なのね
まずは、「追加」のボタンからフォームにsignalを送って見ることにします。追加のボタンからぐいぃ〜っとドラッグすると、電気回路図のグランドみたいなマークが出てきて、離すと次のダイアログが出てきました。
clicked()につながるslotを追加?
signal(pushButton)側に「clicked()」と「clicked(bool)」がありますが、前者は単に後者の引数がfalseに設定されているものの様です。この引数は、ボタンが「checkable」の場合に意味があるようです。今回はいらないので、前者でいいですね。
MainWindowClass(QMainWindow)側にslotが何も出ていないので、「Edit」ボタンをクリックして、MainWindowクラスにslotを追加してみましょう。
これで関数を追加ですかね
「Slots」側の「+」ボタンを押して、slot関数を追加します。
ま、名前は普通に
追加されたslotを選択して、「OK」を押します。
OK
左下の「Show signals and slots inherited from QWidget」と言うのが気になりますね・・。試しに、チェックを入れたら、デフォルトで用意されているsignal/slotが全部表示されました。あ~、そう言うことね・・。
関係無かった・・
これは、今はどうでもいいことでした。気を取り直して「OK」をクリック。すると、フォーム上に接続の様子が出てきました。画面が狭いので、見にくいですね・・。
見にくい・・
さて、とりあえず、ここで保存をして、追加したslotである「addButtonClicked()」を編集したいと思います。と思って、ソースを見てみましたが、特に関数は追加されていません・・。どうすりゃええの・・?昔の記事では、手動で追加してるみたいですね。
んんん~。ほんとにそれが正解なのか・・? つづく。