blob: 238ecb196aa31a75776245a1f4e91fcd9e92129f (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
|
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in a
** written agreement between you and Nokia.
**
** GNU Free Documentation License
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of this
** file.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\page tutorials-addressbook.html
\title チュートリアル: アドレス帳
\brief シンプルだが、完全な機能を持つアプリケーションの作成を通じた
GUI プログラミングへの導入。
このチュートリアルでは、Qt クロスプラットフォームフレームワークを使用した
GUI プログラミングの概要について説明しています。
\image addressbook-tutorial-screenshot.png
\omit
It doesn't cover everything; the emphasis is on teaching the programming
philosophy of GUI programming, and Qt's features are introduced as needed.
Some commonly used features are never used in this tutorial.
\endomit
このチュートリアルでは以下に示す Qt の基本的な機能を学ぶことが出来ます:
\list
\o ウィジェットとレイアウトマネージャ
\o コンテナクラス
\o シグナル/スロット
\o 入力デバイスと出力デバイス
\endlist
Qt を初めて利用する方は、最初に \l{How to Learn Qt} をご覧ください。
チュートリアルの目次:
\list 1
\o \l{tutorials/addressbook/part1}{ユーザインターフェースの設計}
\o \l{tutorials/addressbook/part2}{アドレスの追加}
\o \l{tutorials/addressbook/part3}{項目間のナビゲーション}
\o \l{tutorials/addressbook/part4}{アドレスの編集と削除}
\o \l{tutorials/addressbook/part5}{検索機能の追加}
\o \l{tutorials/addressbook/part6}{読み込みと保存}
\o \l{tutorials/addressbook/part7}{その他の機能}
\endlist
このチュートリアルのソースコードは、Qt の
\c examples/tutorials/addressbook ディレクトリに置かれています。
この小さなアプリケーションは本格的な最新の GUI
アプリケーションのようには見えませんが、
複雑なアプリケーションで使用される多くの基本的な技術が採用されています。
このチュートリアルが終わったら、
メニューやツールバー、ステータスバーなどを表示する
小さな GUI アプリケーションである
\l{mainwindows/application}{Application}
のサンプルをチェックすることをお勧めします。
*/
/*!
\page tutorials-addressbook-part1.html
\example tutorials/addressbook/part1
\title 第1章 - ユーザインターフェースのデザイン
本チュートリアルの第1章では
アドレス帳アプリケーションで使用する基本的なインターフェース
(GUI) の設計について説明します。
GUI プログラム作成の最初のステップは、
ユーザインターフェースを設計することです。
本章の目標は、基本的なアドレス帳アプリケーションを実装するのに必要な
ラベルと入力フィールドを設定することです。
下図は、ここで作成しようとしているアドレス帳のスクリーンショットです。
\image addressbook-tutorial-part1-screenshot.png
ユーザがアドレス帳に名前や住所を入力できるようにするには、
2つの QLabel のオブジェクトである \c nameLabel 及び \c addressLabel と、
入力フィールドとして QLineEdit オブジェクトの \c nameLine と
QTextEdit オブジェクトの \c addressText が必要です。
使用するウィジェットとその配置は下図に示してあります。
\image addressbook-tutorial-part1-labeled-screenshot.png
以下の3つのファイルを使用してこのアドレス帳を実装します:
\list
\o \c{addressbook.h} - \c AddressBook クラスを宣言するファイル、
\o \c{addressbook.cpp} - \c AddressBook クラスを実装するファイル、
\o \c{main.cpp} - \c AddressBook クラスのインスタンスを持つ
\c main() 関数を含むファイル。
\endlist
\section1 Qt プログラミング - 派生クラスの作成
Qt プログラムを作成する際は、
通常 Qt オブジェクトの派生クラスを作成して機能を追加します。
これは、カスタムウィジットや標準的なウィジェットの集合体を作成する場合の
重要なコンセプトの1つです。
ウィジェットの挙動の拡張や変更のために派生クラスを作成することには
以下のような利点があります:
\list
\o 仮想関数または純粋仮想関数の実装を行って要求される機能を実装できます。
必要によっては基底クラスの実装にフォールバックして利用できます。
\o これにより、アプリケーションのその他の部分が
ユーザインターフェースの個々のウィジェットを指定する必要がないよう、
ユーザインターフェースの一部をクラス内にカプセル化できます。
\o 派生クラスは、同一のアプリケーションまたはライブラリに
複数のカスタムウィジットを作成するために使用できます。
また、派生クラスのコードは、他のプロジェクトで再利用可能です。
\endlist
Qt ではアドレス帳ウィジェットを提供していないため、
標準的な Qt ウィジェットクラスを継承して機能を追加します。
本チュートリアルで作成する \c AddressBook クラスは、
基本的なアドレス帳ウィジェットが必要な場合に再利用できます。
\section1 AddressBook クラスの宣言
\l{tutorials/addressbook/part1/addressbook.h}{\c addressbook.h}
ファイルは、 \c AddressBook クラスを宣言するために使用します。
ここでは \c AddressBook を QWidget の派生クラスとし、
コンストラクタを宣言することから始めます。
また、 Q_OBJECT マクロを使用して、クラスで多言語化機能及び、
Qt のシグナル/スロット機能が使われていることを示します。
ただし、現段階ではこれらの機能の全てを使わない場合もあります。
\snippet tutorials/addressbook/part1/addressbook.h class definition
クラスは、 前述した QLineEdit と QTextEdit のプライベートなインスタンス
\c nameLine と \c addressText の宣言を保有します。
後述の章で、 \c nameLine 及び \c addressText に保存したデータが、
多くのアドレス帳の機能で必要になることがわかるでしょう。
我々が使用する QLabel オブジェクトは、
一旦作成すると後で参照する必要がないため、宣言する必要はありません。
Qt でオブジェクトの所有権を追跡する方法については、次節で説明します。
Q_OBJECT マクロ自体は、Qt のより高度な機能を実装します。
ここでは、 Q_OBJECT マクロを、 \l{QObject::}{tr()} 及び
\l{QObject::}{connect()} 関数を使用可能にする
ショートカットとみなしたほうが良いでしょう。
これで、 \c addressbook.h ファイルが完成しました。
次は対応する \c addressbook.cpp ファイルの実装を行います。
\section1 AddressBook クラスの実装
\c AddressBook のコンストラクタは、 \a{parent} パラメータとして
QWidget を受け取ります。
慣例により、このパラメータを基底クラスのコンストラクタに渡します。
親が複数の子を所有できるというこの所有概念は、
Qt でウィジェットのグループ化を行う場合に有用です。
例えば、親を削除すると、親に属する子も全て削除されます。
\snippet tutorials/addressbook/part1/addressbook.cpp constructor and input fields
このコンストラクタ内で、 \c nameLine と \c addressText のインスタンスを生成し、
2つのローカル QLabel オブジェクトの \c nameLabel と \c addressLabel
を宣言し、そのインスタンスを生成します。
\l{QObject::tr()}{tr()} 関数は、
翻訳した文字列が存在する場合にそれを返します。
そうでない場合は、引数の文字列自体を返します。
この関数は文字列が他の言語に翻訳されるようにマークする役目を持ちます。
翻訳可能な文字列を使用するたびに、マークしてください。
Qt を使用してプログラミングを行う場合、
レイアウトが機能する仕組みを理解していると便利です。
Qt は主に、以下の3つのレイアウトクラスを提供します。
ウィジェットの配置を処理するための QHBoxLayout 、 QVBoxLayout 及び
QGridLayout です。
\image addressbook-tutorial-part1-labeled-layout.png
ここでは、 QGridLayout を使って、
ラベルと入力フィールドを配置します。
QGridLayout は、有効なスペースを格子状に分割し、
行/列番号により指定するセルに、ウィジェットを配置します。
上図は、レイアウトセルとウィジェットの位置を示しており、
以下のコードを使用して、この配置を指定します:
\snippet tutorials/addressbook/part1/addressbook.cpp layout
\c addressLabel は、追加引数として Qt::AlignTop を用いて配置されていることに注意してください。
これは、セル(1,0) で縦方向の中央に配置されないようにするためです。
Qt のレイアウトにおける基本概念については、
\l{Layout Management} のドキュメントをご覧ください。
ウィジェットにレイアウトオブジェクトをインストールするには、
ウィジェットの \l{QWidget::setLayout()}{setLayout()}
関数を呼び出す必要があります。
\snippet tutorials/addressbook/part1/addressbook.cpp setting the layout
最後に、ウィジェットのタイトルを "Simple Address Book" とします。
\section1 アプリケーションを実行する
別のファイル \c main.cpp に、 \c main() 関数に実装します。
この関数内で、QApplication である \c app のインスタンスを生成します。
QApplication は、既定のフォントやカーソルなど
アプリケーションに共通な複数のリソース及び、
イベントループの実行に関与しています。
従って、Qt を使用する全ての GUI アプリケーションでは、
必ず QApplication が存在します。
\snippet tutorials/addressbook/part1/main.cpp main function
スタックに新しい \c AddressBook ウィジェットを生成し、
\l{QWidget::show()}{show()} 関数を呼び出して表示します。
ただし、ウィジェットはアプリケーションのイベントループが
開始されるまで表示されません。
アプリケーションの \l{QApplication::}{exec()} 関数を呼び出して、
イベントループを開始します。
この関数により返された結果は、 \c main() 関数の戻り値として使用されます。
この時点で、スタックで \c AddressBook
をインスタンス化した理由が明らかになります。
イベントループが終了すると \c main() 関数のスコープの外に移動します。
それに伴って \c AddressBook 及び、
これに属する全ての子ウィジェットが削除されるため、
メモリリークを防ぐことができます。
*/
/*!
\page tutorials-addressbook-part2.html
\example tutorials/addressbook/part2
\title 第2章 - アドレスの追加
基本的なアドレス帳アプリケーションを作成するための次の手順は、
ユーザからの操作を可能にすることです。
\image addressbook-tutorial-part2-add-contact.png
新しい連絡先を追加するために、
ユーザがクリックするプッシュボタンを作成します。
また、これらの連絡先を整理して保存するために、
何らかのデータ構造が必要になります。
\section1 AddressBook クラスの宣言
ラベルと入力フィールドの設定が完了しているため、
連絡先を追加する際に必要なプッシュボタンを追加します。
\c addressbook.h ファイルに、
3つの QPushButton オブジェクトを宣言し、
対応する3つの public slot を作成します。
\snippet tutorials/addressbook/part2/addressbook.h slots
スロットとは特定のシグナルに応答して呼び出される関数をいいます。
このコンセプトについては、 \c AddressBook
クラスを実装する際にさらに詳細に取り上げます。
なお、Qt のシグナル/スロットのコンセプトの概要については、
\l{Signals and Slots} のドキュメントを参照してください。
3つの QPushButton オブジェクト \c addButton 、 \c submitButton 、
\c cancelButton は、前章の \c nameLine と \c addressText とともに、
プライベート変数宣言に含まれています。
\snippet tutorials/addressbook/part2/addressbook.h pushbutton declaration
アドレス帳の連絡先を横断して表示するには、
連絡先保持用のコンテナが必要です。
QMap のオブジェクトである \c contacts をこの目的に使用します。
\c contacts にはキーとして連絡先の名前 \e key
とそれに対応する値として連絡先の住所 \e value を格納します。
\snippet tutorials/addressbook/part2/addressbook.h remaining private variables
QString の2つのプライベートオブジェクトとして \c oldName と \c oldAddress
も宣言します。
これらのオブジェクトは、ユーザが \gui Add
をクリックする前に最後に表示した連絡先の名前と住所が含まれている必要があります。
ユーザが \gui Cancel
をクリックすると、最後に表示した連絡先の詳細に戻って表示することが出来ます。
\section1 AddressBook クラスの実装
\c AddressBook のコンストラクタ内で、連絡先の詳細を編集することなく
表示のみを行えるよう、 \c nameLine と \c addressText
を読み取り専用に設定します。
\dots
\snippet tutorials/addressbook/part2/addressbook.cpp setting readonly 1
\dots
\snippet tutorials/addressbook/part2/addressbook.cpp setting readonly 2
次に、プッシュボタン \c addButton 、 \c submitButton 、 \c cancelButton
のインスタンスを生成します。
\snippet tutorials/addressbook/part2/addressbook.cpp pushbutton declaration
\c addButton を、 \l{QPushButton::show()} 関数を呼び出して表示します。
\c submitButton と \c cancelButton は、
\l{QPushButton::hide()}{hide()} を呼び出して非表示にします。
これら2つのプッシュボタンはユーザが \gui Add
をクリックした場合にのみ表示され、後に説明する
\c addContact() によって処理されます。
\snippet tutorials/addressbook/part2/addressbook.cpp connecting signals and slots
各プッシュボタンの \l{QPushButton::clicked()}{clicked()} を、
それぞれのスロットに接続します。
下図はこれを説明したものです。
\image addressbook-tutorial-part2-signals-and-slots.png
次に、 QVBoxLayout を使用して、
アドレス帳ウィジェットの右側にプッシュボタンを
上下一列に配置します。
\snippet tutorials/addressbook/part2/addressbook.cpp vertical layout
\l{QBoxLayout::addStretch()}{addStretch()} 関数は、
プッシュボタンを等間隔に並べるのではなく、
ウィジェット上部に近づけて配置するために使用します。
下図は、\l{QBoxLayout::addStretch()}{addStretch()} を使用した場合と、
使用しない場合の違いを示しています。
\image addressbook-tutorial-part2-stretch-effects.png
次に、\l{QGridLayout::addLayout()}{addLayout()} を使用して、
\c buttonLayout1 を \c mainLayout に追加します。
\c buttonLayout1 は \c mainLayout の子となり、
レイアウトが入れ子になりました。
\snippet tutorials/addressbook/part2/addressbook.cpp grid layout
レイアウトの配置は以下のようになります:
\image addressbook-tutorial-part2-labeled-layout.png
\c addContact() では、最後に表示した連絡先の詳細を、
\c oldName と \c oldAddress に保存します。
次に、これらの入力フィールドをクリアし、読み取り専用モードを解除します。
\c nameLine にフォーカスをセットして、 \c submitButton と \c cancelButton を表示します。
\snippet tutorials/addressbook/part2/addressbook.cpp addContact
\c submitContact() 関数は、以下の次の3つの要素から成ります:
\list 1
\o \c nameLine 及び \c addressText から連絡先の詳細を抽出し、
QString オブジェクトに保存します。
また、ユーザが入力フィールドに何も入力せずに
\gui Submit をクリックしないよう確認します。
どちらかのフィールドが空であれば、ユーザに名前と住所の入力を求める
QMessageBox が表示されます。
\snippet tutorials/addressbook/part2/addressbook.cpp submitContact part1
\o 続けて、連絡先が既に存在するかどうか確認します。
存在しない場合は \c contacts に連絡先を追加して QMessageBox を表示し、
連絡先が追加されたことをユーザに知らせます。
\snippet tutorials/addressbook/part2/addressbook.cpp submitContact part2
連絡先が既に存在する場合、 QMessageBox を表示し、
連絡先が重複して追加されないように
連絡先が存在することをユーザに知らせます。
\c contacts オブジェクトは、名前と住所のキー値のペアで構成されているため、
\e key が一意であることを確認します。
\o 上記の両方の事例に対処したら、
以下のコードを使ってプッシュボタンを通常の状態に戻します:
\snippet tutorials/addressbook/part2/addressbook.cpp submitContact part3
\endlist
以下のスクリーンショットは、ユーザに情報メッセージを表示するために使用する
QMessageBox オブジェクトです。
\image addressbook-tutorial-part2-add-successful.png
\c cancel() 関数は、最後に表示した連絡先の詳細を復元して
\c addButton を有効にし、 \c submitButton と \c cancelButton
を非表示にします。
\snippet tutorials/addressbook/part2/addressbook.cpp cancel
連絡先を追加する際の概念は、ユーザがいつでも自由に
\gui{送信} または \gui{キャンセル} をクリックできるようにすることです。
以下のフローチャートは、この概念を詳細に説明しています:
\image addressbook-tutorial-part2-add-flowchart.png
*/
/*!
\page tutorials-addressbook-part3.html
\example tutorials/addressbook/part3
\title 第3章 - 項目間のナビゲーション
アドレス帳アプリケーションは、半分完成しました。
次に表示する連絡先を変更するために、いくつかの関数を追加します。
しかし、最初に、これらの連絡先を保存するために使用するデータ構造の種類を決める必要があります。
第2章では、連絡先の名前 \e key 及び、連絡先の住所 \e value
の鍵と値のペアで QMap を使用しました。
これは、この事例で使用するのに適した構造です。
ただし、各項目間を移動して表示するには、多少の機能拡張が必要です。
QMap を、最初の要素と最後の要素を含むすべての要素が接続された
循環リストに似たデータ構造に、機能拡張します。
下図はこのデータ構造を説明したものです。
\image addressbook-tutorial-part3-linkedlist.png
\section1 AddressBook クラスの宣言
アドレス帳アプリケーションにナビゲーション機能を追加するには、
\c AddressBook クラスに2つのスロットを追加する必要があります。
next() 及び \c previous() を
\c addressbook.h ファイルに追加します。
\snippet tutorials/addressbook/part3/addressbook.h navigation functions
また、新たに2つの QPushButton オブジェクトが必要になります。
プライベート変数として
\c nextButton と \c previousButton を宣言します。
\snippet tutorials/addressbook/part3/addressbook.h navigation pushbuttons
\section1 AddressBook クラスの実装
\c addressbook.cpp の \c AddressBook コンストラクタでは、
\c nextButton と \c previousButton のインスタンスを生成し
初期設定で無効にします。
これは、アドレス帳に複数の連絡先が存在しない場合、
ナビゲーションを有効にしないためです。
\snippet tutorials/addressbook/part3/addressbook.cpp navigation pushbuttons
これらのプッシュボタンを、それぞれスロットに接続します:
\snippet tutorials/addressbook/part3/addressbook.cpp connecting navigation signals
以下の画像は、ここで作成しようとしている
グラフィカルユーザインターフェースです。
最終アプリケーションが完成間近であることがわかるでしょう。
\image addressbook-tutorial-part3-screenshot.png
\c next() と \c previous() では基本的な規約に従って、
\c nextButton を右側に、 \c previousButton を左側に配置します。
直感的でわかりやすいレイアウトに仕上げるために、
QHBoxLayout を使用してウィジェットを横に並べて配置します:
\snippet tutorials/addressbook/part3/addressbook.cpp navigation layout
次に、 \c mainLayout に、QHBoxLayout のオブジェクトである
\c buttonLayout2 を追加します。
\snippet tutorials/addressbook/part3/addressbook.cpp adding navigation layout
下図は、 \c mainLayout のウィジェットの配置を示します。
\image addressbook-tutorial-part3-labeled-layout.png
\c addContact() 関数内部では、
ユーザが連絡先を追加しているときに移動しないよう、
これらのボタンを無効にする必要があります。
\snippet tutorials/addressbook/part3/addressbook.cpp disabling navigation
また、 \c submitContact() 関数では、 \c contacts のサイズに応じて、
ナビゲーションボタン \c nextButton 及び \c previousButton を有効にします。
前述のように、アドレス帳に複数の連絡先が存在しない場合は、
ナビゲーションを有効にできません。
以下のコードは、その処理方法を示しています。
\snippet tutorials/addressbook/part3/addressbook.cpp enabling navigation
\c cancel() 関数にも、これらの処理を含めます。
ここで、QMap のオブジェクト \c contacts を使って、
循環リストをエミュレートしましょう。
\c next() 関数で、 \c contacts のイテレータを作成し、次に:
\list
\o イテレータが \c contacts の末尾にない場合は、次の項目に移動します。
\o イテレータが \c contacts の末尾にある場合は、
\c contacts の先頭に移動します。
これによって、QMap が循環リストのように機能するという印象を与えます。
\endlist
\snippet tutorials/addressbook/part3/addressbook.cpp next() function
\c contacts 内の正しいオブジェクトに移動したら、
\c nameLine と \c addressText に内容を表示します。
同様に、 \c previous() 関数で、 \c contacts のイテレータを作成し、次に:
\list
\o イテレータが \c contacts の末尾にある場合は、
表示をクリアして戻ります。
\o イテレータが \c contacts の先頭にある場合は、末尾に移動します。
\o イテレータを直前の項目に移動します。
\endlist
\snippet tutorials/addressbook/part3/addressbook.cpp previous() function
もう一度、 \c contacts に現在のオブジェクトの内容を表示します。
*/
/*!
\page tutorials-addressbook-part4.html
\example tutorials/addressbook/part4
\title 第4章 - アドレスの編集と削除
本章では、アドレス帳アプリケーションに保存されている
連絡先の内容を変更する方法について説明します。
\image addressbook-tutorial-screenshot.png
ここでは、連絡先を整理して保存するだけでなく、
ナビゲーションが可能なアドレス帳を作成します。
必要に応じて連絡先の詳細を変更できるよう、
編集・削除機能を追加すると便利です。
ただし、列挙型書式の若干の改善が必要になります。
前章では、次の2つのモードを扱いました:
\c AddingMode 及び \c NavigationMode -
ただし、これらは列挙型として定義しませんでした。
代わりに、対応するボタンを手動で有効または無効にし、
複数行のコードを繰り返します。
本章では、以下のような2つの異なる値を持つ \c Mode 列挙型を定義します:
\list
\o \c{NavigationMode}
\o \c{AddingMode}
\o \c{EditingMode}
\endlist
\section1 AddressBook クラスの宣言
\c addressbook.h ファイルをアップデートし、 \c Mode 列挙型を追加します:
\snippet tutorials/addressbook/part4/addressbook.h Mode enum
また、public slot として2つの新しいスロット
\c editContact() 及び \c removeContact() を追加します。
\snippet tutorials/addressbook/part4/addressbook.h edit and remove slots
モードを切り替えるために、すべての QPushButton オブジェクトの
有効化/無効化を制御する \c updateInterface() 関数を導入します。
また、前述の編集と削除を行うスロット用に、2つの新しいボタン
\c editButton 及び \c removeButton を追加します。
\snippet tutorials/addressbook/part4/addressbook.h updateInterface() declaration
\dots
\snippet tutorials/addressbook/part4/addressbook.h buttons declaration
\dots
\snippet tutorials/addressbook/part4/addressbook.h mode declaration
最後に、現在のモードを保持する列挙型の \c currentMode を宣言します。
\section1 AddressBook クラスの実装
ここで、アドレス帳アプリケーションのモード変更機能を実装する必要があります。
アドレス帳をはじめて起動するときは、
メモリに連絡先が保存されていないため、
コンストラクタでは、 \c editButton 及び \c removeButton
のインスタンスの生成と無効化を行います。
\snippet tutorials/addressbook/part4/addressbook.cpp edit and remove buttons
また、ボタンをそれぞれのスロット \c editContact() 及び \c removeContact()
に接続し、 \c buttonLayout1 に追加します。
\snippet tutorials/addressbook/part4/addressbook.cpp connecting edit and remove
\dots
\snippet tutorials/addressbook/part4/addressbook.cpp adding edit and remove to the layout
\c editContact() は、モードを \c EditingMode に切り替える前に、
連絡先の古い詳細情報を、 \c oldName と \c oldAddress に保存します。
このモードでは、 \c submitButton と \c cancelButton
の両方が有効になるため、ユーザは連絡先の詳細を変更し、
どちらか一方のボタンをクリックできます。
\snippet tutorials/addressbook/part4/addressbook.cpp editContact() function
\c submitContact() 関数は、 \c{if-else} ステートメントにより、
2つの処理に分割しています。
\c currentMode が \c AddingMode であるかどうかを確認します。
その場合、連絡先追加の処理を行います。
\snippet tutorials/addressbook/part4/addressbook.cpp submitContact() function beginning
\dots
\snippet tutorials/addressbook/part4/addressbook.cpp submitContact() function part1
\c AddingMode でない場合、 \c currentMode が \c EditingMode
であるかどうか確認します。
その場合、 \c oldName を \c name と比較します。
名前を変更した場合、 \c contacts から古い連絡先を削除し、
新たに更新した連絡先を挿入します。
\snippet tutorials/addressbook/part4/addressbook.cpp submitContact() function part2
住所のみを変更した場合 (たとえば、 \c oldAddress は \c address と異なる)、
連絡先の住所を更新します。
最後に、 \c currentMode を \c NavigationMode に設定します。
これは、無効にしたプッシュボタンをすべて再び有効にするための重要なステップです。
アドレス帳から連絡先の削除を行う
\c removeContact() 関数を実装します。
この関数では、まず連絡先が \c contacts に存在するかどうか確認します。
\snippet tutorials/addressbook/part4/addressbook.cpp removeContact() function
存在する場合、ユーザに削除を確認するために、QMessageBox を表示します。
ユーザが確認したら、 \c previous() を呼び出して、
ユーザインターフェースで他の連絡先を表示できることを確認し、
QMap の \l{QMap::remove()}{remove()} 関数を使用して連絡先を削除します。
念のため、QMessageBox を表示してユーザに知らせます。
この関数で使用するメッセージボックスを以下に示します:
\image addressbook-tutorial-part4-remove.png
\section2 ユーザインターフェースを更新する
既に、 \c updateInterface() 関数が
現在のモードに応じてプッシュボタンの有効化と無効化を行うための
手段であることを説明しました。
この関数は、渡される \c mode 引数に応じて現在のモードを更新します。
なお、引数の値を確認する前に \c currentMode に代入しています。
このとき、それぞれのプッシュボタンは、
現在のモードに応じて有効または無効になります。
\c AddingMode 及び \c EditingMode でのコードを以下に示します:
\snippet tutorials/addressbook/part4/addressbook.cpp update interface() part 1
\c NavigationMode の場合、 QPushButton::setEnabled()
関数のパラメータに条件を含めます。
これは、アドレス帳に少なくとも1つ以上の連絡先が存在する場合に、
\c editButton と \c removeButton が有効であることを確認するためのものです。
\c nextButton と \c previousButton は、
アドレス帳に少なくとも2つ以上の連絡先が存在する場合にのみ有効になります。
\snippet tutorials/addressbook/part4/addressbook.cpp update interface() part 2
モードの設定及び、同一関数内でのユーザインターフェースの更新を行うタスクを実行することにより、
アプリケーションの内部状態により
ユーザインターフェースが「非同期」になる可能性を防ぎます。
*/
/*!
\page tutorials-addressbook-part5.html
\example tutorials/addressbook/part5
\title 第5章 - 検索機能の追加
本章では、アドレス帳アプリケーションの連絡先と住所を
検索する方法について説明します。
\image addressbook-tutorial-part5-screenshot.png
アドレス帳アプリケーションへ連絡先の追加を繰り返すと、
\e Next 及び \e Previous ボタンによる連絡先のナビゲートが面倒になります。
この場合、\e{検索(Find)}機能を使うと、
連絡先の検索をより効率的に行うことができます。
上記スクリーンショットは、\e Find ボタン及び、
パネル上のボタンの位置を示します。
ユーザーが \e Find ボタンをクリックすると、
連絡先の名前の入力を求めるダイアログを表示します。
Qt が提供する QDialog クラスを継承して \c FindDialog クラスを導入します。
\section1 FindDialog クラスの宣言
\image addressbook-tutorial-part5-finddialog.png
QDialog を継承するには、最初に、QDialog のヘッダーを
\c finddialog.h ファイルで include する必要があります。
また、これらのウィジェットをダイアログクラスで使用するため、
前方宣言を使用して QLineEdit と QPushButton を宣言します。
\c AddressBook クラスと同様に、 \c FindDialog クラスには Q_OBJECT
マクロが含まれ、ダイアログが別のウィンドウとして開かれる場合でも、
コンストラクタは親 QWidget を取るように定義されます。
\snippet tutorials/addressbook/part5/finddialog.h FindDialog header
\c FindDialog のインスタンスを保持するクラスにより使用される
パブリック関数 \c getFindText() を定義します。
この関数を経由してユーザが入力した検索文字列を取得します。
また、ユーザが \gui Find ボタンをクリックしたときに検索文字列を処理するために、
public slot として \c findClicked() を定義します。
最後に、\gui Find ボタンに対応するプライベート変数として \c findButton 、
ユーザが検索文字列を入力するラインエディット \c lineEdit 及び、
後の作業で使用する検索文字列を保存するために使用する内部文字列として
\c findText の定義を行います。
\section1 FindDialog クラスの実装
\c FindDialog のコンストラクタ内で、プライベート変数の
\c lineEdit 、 \c findButton 及び \c findText を設定します。
QHBoxLayout を用いてウィジェットを配置します。
\snippet tutorials/addressbook/part5/finddialog.cpp constructor
シグナルをそれぞれのスロットに接続し、
レイアウトとウィンドウのタイトルを設定します。
\c findButton の \l{QPushButton::clicked()} シグナルは、
\c findClicked() と \l{QDialog::accept()}{accept()}
に接続されていることがわかります。
QDialog により提供される \l{QDialog::accept()}{accept()} スロットは
ダイアログを非表示にして、結果コードを \l{QDialog::}{Accepted}
に設定します。
この関数を使用して、 \c AddressBook の \c findContact() 関数に、
\c FindDialog オブジェクトが閉じていることを知らせます。
この論理については、 \c findContact() 関数について取り上げる際に、
さらに詳細に説明します。
\image addressbook-tutorial-part5-signals-and-slots.png
\c findClicked() で、ユーザが連絡先の名前を入力せずに
\gui Find ボタンをクリックしたかどうか確認するために
\c lineEdit を検証します。
次に、 \c lineEdit から抽出した検索文字列を \c findText に設定します。
その後、 \c lineEdit のコンテンツをクリアし、ダイアログを非表示にします。
\snippet tutorials/addressbook/part5/finddialog.cpp findClicked() function
\c findText 変数用のパブリックな取得関数
\c getFindText() を実装します。
コンストラクタ及び \c findClicked() 関数でのみ
\c findText を直接設定するため、
\c getFindText() に対応する設定関数は作成しません。
\c getFindText() はパブリックであるため、インスタンスを生成し
\c FindDialog を使用するクラスは、
ユーザが入力して確定した検索文字列にいつでもアクセスできます。
\snippet tutorials/addressbook/part5/finddialog.cpp getFindText() function
\section1 AddressBook クラスの宣言
\c AddressBook クラス内部から \c FindDialog を使用できるよう、
\c addressbook.h ファイルに \c finddialog.h を含めます。
\snippet tutorials/addressbook/part5/addressbook.h include finddialog's header
これまでのところ、アドレス帳の全機能に、
QPushButton とそのボタンに対応するスロットが含まれています。
同様に、\gui{検索(Find)}機能には、
\c findButton 及び \c findContact() が含まれています。
\c findButton はプライベート変数として宣言され、
\c findContact() 関数は public slot として宣言されます。
\snippet tutorials/addressbook/part5/addressbook.h findContact() declaration
\dots
\snippet tutorials/addressbook/part5/addressbook.h findButton declaration
最後に、 \c FindDialog のインスタンスを参照するためのプライベート変数
\c dialog を宣言します。
\snippet tutorials/addressbook/part5/addressbook.h FindDialog declaration
インスタンス化されたダイアログは複数回使用します。
プライベート変数を使用すると、
クラスの複数の場所から参照できるようになります。
\section1 AddressBook クラスの実装
\c AddressBook クラスのコンストラクタ内で、
プライベートオブジェクトの \c findButton と \c findDialog
のインスタンスを生成します:
\snippet tutorials/addressbook/part5/addressbook.cpp instantiating findButton
\dots
\snippet tutorials/addressbook/part5/addressbook.cpp instantiating FindDialog
次に、 \c findButton の \l{QPushButton::clicked()}{clicked()} シグナルを、
\c findContact() に接続します。
\snippet tutorials/addressbook/part5/addressbook.cpp signals and slots for find
後は、 \c findContact() 関数のコードだけです:
\snippet tutorials/addressbook/part5/addressbook.cpp findContact() function
\c FindDialog インスタンスの \c dialog を表示することから始めます。
これは、ユーザが検索用に連絡先の名前を入力するためのものです。
ユーザがダイアログの \c findButton をクリックすると
ダイアログが非表示になり、結果コードが QDialog::Accepted に設定されます。
これにより、 \c if ステートメントが常に真になります。
続けて、検索文字列の抽出を行います。
この場合、検索文字列は \c contactName で、
\c FindDialog の \c getFindText() 関数を使用して取得します。
アドレス帳に連絡先が存在する場合、直ちに表示します。
存在しない場合、検索が失敗したことを示す以下の QMessageBox が表示されます。
\image addressbook-tutorial-part5-notfound.png
*/
/*!
\page tutorials-addressbook-part6.html
\example tutorials/addressbook/part6
\title 第6章 - 読み込みと保存
本章では、アドレス帳アプリケーションの読み込みと保存処理で使用する、
Qt のファイル処理機能について説明します。
\image addressbook-tutorial-part6-screenshot.png
連絡先の参照及び検索機能は便利な機能ですが、
連絡先の保存と読み込みが可能になるまではアドレス帳は完成しません。
Qt は \l{Input/Output and Networking}{入出力}
用のクラスを様々提供していますが、
今回は簡単に組み合わせて使用できる QFile と QDataStream の2つのクラスを選択しました。
QFile のオブジェクトは、
読み込みと書き込みが可能なディスク上のファイルを表します。
QFile は、さまざまなデバイスを表すより一般的な QIODevice クラスの派生クラスです。
QDataStream オブジェクトは、バイナリデータを QIODevice に保存して、
後でもう一度取得できるようシリアライズするために使用します。
パラメータとしてそれぞれのデバイスを使用すると、
ストリームを開くのと同じくらい簡単に、 QIODevice
からの読み込みと書き込みを行うことができます。
\section1 AddressBook クラスの宣言
2つの QPushButton オブジェクトの \c loadButton と \c saveButton
に加えて、2つの public slot の \c saveToFile() と \c loadFromFile()
を宣言します。
\snippet tutorials/addressbook/part6/addressbook.h save and load functions declaration
\dots
\snippet tutorials/addressbook/part6/addressbook.h save and load buttons declaration
\section1 AddressBook クラスの実装
コンストラクタで、 \c loadButton と \c saveButton のインスタンスを生成します。
理想的には、プッシュボタンのラベルを、
"Load contacts from a file"(「ファイルから連絡先を読み込む」)及び、
"Save contacts to a file"(「連絡先をファイルに保存する」)
に設定するほうがよりユーザーフレンドリーです。
ただし、他のプッシュボタンのサイズを考慮して、
\gui{Load...}及び\gui{Save...}に設定します。
Qt は、 \l{QWidget::setToolTip()}{setToolTip()} を使用して
ツールチップを簡単に設定する方法を提供していますので、
以下のコードでプッシュボタンにツールチップを設定します:
\snippet tutorials/addressbook/part6/addressbook.cpp tooltip 1
\dots
\snippet tutorials/addressbook/part6/addressbook.cpp tooltip 2
ここには記載していませんが、
これまで実装した他の機能のように右側のレイアウトパネル
\c button1Layout にプッシュボタンを追加し、
プッシュボタンの \l{QPushButton::clicked()}{clicked()}
シグナルをそれぞれのスロットに接続します。
保存機能の場合、最初に QFileDialog::getSaveFileName() を使用して、
\c fileName を取得します。
これは、QFileDialog により提供される簡易関数で、
モーダルなファイルダイアログをポップアップ表示させ、
ユーザはファイル名を入力したり、既存の \c{.abk} ファイルを選択することが出来ます。
\c{.abk} ファイルは、
連絡先を保存したときに作成されるアドレス帳の拡張子です。
\snippet tutorials/addressbook/part6/addressbook.cpp saveToFile() function part1
以下のスクリーンショットのようなネイティブのファイルダイアログが表示されます:
\image addressbook-tutorial-part6-save.png
\c fileName が空ではない場合、 \c fileName を用いて
QFile のオブジェクト \c file を作成します。
QFile は QIODevice の派生クラスであるため、
QDataStream から使用できます。
次に、ファイルを \l{QIODevice::}{WriteOnly} モードでオープンします。
オープンに失敗した場合、 QMessageBox を表示してユーザに知らせます。
\snippet tutorials/addressbook/part6/addressbook.cpp saveToFile() function part2
オープンに成功した場合、 QDataStream のインスタンス \c out を生成して、
開いているファイルに書き込みます。
QDataStream では、同じバージョンのストリームを使用して、
読み込みと書き込みを行う必要があります。
\c file にシリアライズする前に、
\l{QDataStream::Qt_4_5}{Qt 4.5で導入されたバージョン}
を今回使用するバージョンとして設定します。
\snippet tutorials/addressbook/part6/addressbook.cpp saveToFile() function part3
読み込み機能の場合も QFileDialog::getOpenFileName() を使用して
\c fileName を取得します。
この関数は QFileDialog::getSaveFileName() に対応するものであり、
モーダルなファイルダイアログをポップアップ表示させ、
ユーザはファイル名を入力したり、既存の \c{.abk}
ファイルを選択してアドレス帳に読み込ことができます。
\snippet tutorials/addressbook/part6/addressbook.cpp loadFromFile() function part1
たとえば、 Windows では、この関数は、以下のスクリーンショットのような
ネイティブのファイルダイアログをポップアップ表示させます。
\image addressbook-tutorial-part6-load.png
\c fileName が空ではない場合、 QFile のオブジェクト
\c file を使用して、 \l{QIODevice::}{ReadOnly} モードでオープンします。
\c saveToFile() の実装の場合と同様に、オープンに失敗した場合は
QMessageBox を表示して、ユーザに知らせます。
\snippet tutorials/addressbook/part6/addressbook.cpp loadFromFile() function part2
オープンに成功した場合、
QDataStream のインスタンス \c in を生成して前記のようにバージョン設定を行い、
シリアライズしたデータを \c contacts のデータ構造に読み込みます。
\c contacts オブジェクトはデータが読み込まれる前に空にするので、
ファイルの読み込みプロセスは簡素化されます。
より高度な方法では、一時オブジェクトの QMap に読み込んで、
重複していない連絡先を \c contacts にコピーします。
\snippet tutorials/addressbook/part6/addressbook.cpp loadFromFile() function part3
ファイルから読み込んだ連絡先を表示するには、
最初に取得したデータを検証し、
読み込んだファイルにアドレス帳の連絡先が実際に含まれているか確認します。
含まれている場合、最初の連絡先を表示します。
含まれていない場合、QMessageBox を表示して、
問題があることをユーザに知らせます。
最後にインターフェースを更新し、適宜
プッシュボタンの有効化と無効化を行います。
*/
/*!
\page tutorials-addressbook-part7.html
\example tutorials/addressbook/part7
\title 第7章 - 追加機能
本章では、アドレス帳アプリケーションを
普段の生活で便利に使用するための追加機能について説明します。
\image addressbook-tutorial-part7-screenshot.png
アドレス帳アプリケーションは、それ自体で便利ですが、
他のアプリケーションとデータの交換ができればなお便利です。
vCard 形式は、この目的で使用可能な一般的なファイル形式です。
本章では、連絡先を vCard ファイル(\c{.vcf})
にエクスポートできるよう、アドレス帳クライアントを拡張します。
\section1 AddressBook クラスの宣言
QPushButton のオブジェクト \c exportButton 及び、
対応する public slot の \c exportAsVCard() を
\c addressbook.h ファイルの \c AddressBook クラスに追加します。
\snippet tutorials/addressbook/part7/addressbook.h exportAsVCard() declaration
\dots
\snippet tutorials/addressbook/part7/addressbook.h exportButton declaration
\section1 AddressBook クラスの実装
\c AddressBook のコンストラクタで、 \c exportButton の
\l{QPushButton::clicked()}{clicked()} シグナルを
\c exportAsVCard() に接続します。
このボタンも、右側のボタンのパネルの処理を行うレイアウトである
\c buttonLayout1 に追加します。
\c exportAsVCard() 関数では、
連絡先の名前を \c name に抽出することから始めます。
\c firstName 、 \c lastName 及び \c nameList を宣言します。
次に \c name を検索し、最初の空白のインデックスを取得します。
空白が存在する場合、連絡先の名前を \c firstName と
\c lastName に分割します。
次に、空白をアンダースコア ("_") に置き換えます。
空白が存在しない場合は、連絡先にファーストネーム(名)
しか保存されていない可能性があります。
\snippet tutorials/addressbook/part7/addressbook.cpp export function part1
\c saveToFile() 関数と同様に、ユーザがファイルの場所を選択できるよう
ファイルダイアログを開きます。
選択したファイル名を使用して、
書き込みを行うための QFile のインスタンスを生成します。
ファイルを \l{QIODevice::}{WriteOnly} モードでオープンします。
オープンに失敗した場合、 QMessageBox を表示して、
問題があることをユーザに知らせて戻ります。
オープンに成功した場合、 QTextStream のオブジェクト \c out へ
パラメータファイルを渡します。
QDataStream のように、QTextStream クラスは、
プレーンテキストファイルの読み込みと書き込みを行う機能を提供します。
結果として、生成された \c{.vcf} は
テキストエディターで開いて編集することができます。
\snippet tutorials/addressbook/part7/addressbook.cpp export function part2
まず \c{BEGIN:VCARD} タグ、次に \c{VERSION:2.1} タグの順に
vCard ファイルに書き込みます。
連絡先の名前は、 \c{N:} タグを使用して書き込みます。
vCard の "File as" プロパティを設定する \c{FN:} タグを書き込む際は、
連絡先にラストネーム(姓)が含まれているかどうかを確認する必要があります。
含まれている場合、 \c nameList の情報を使用して入力します。
含まれていない場合、 \c firstName のみを書き込みます。
\snippet tutorials/addressbook/part7/addressbook.cpp export function part3
続けて連絡先の住所の書き込みを行います。
住所のセミコロンには、"\\" をエスケープ文字として追加する必要があります。
改行はセミコロンに置き換えられ、コンマはスペースに置き換えられます。
次に、 \c{ADR;HOME:;} タグ、 \c address 、
\c{END:VCARD} タグの順に書き込みます。
\snippet tutorials/addressbook/part7/addressbook.cpp export function part4
最後に、vCard のエクスポートに成功したことをユーザに知らせるために、
QMessageBox が表示されます。
\e{vCard は、\l{http://www.imc.org}{Internet Mail Consortium} の登録商標です。}
*/
|