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
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- /home/espenr/tmp/qt-3.3.8-espenr-2499/qt-x11-free-3.3.8/tools/designer/book/chap-creating-database-applications.leaf:3 -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Creating Database Applications</title>
<style type="text/css"><!--
fn { margin-left: 1cm; text-indent: -1cm; }
a:link { color: #004faf; text-decoration: none }
a:visited { color: #672967; text-decoration: none }
body { background: #ffffff; color: black; }
--></style>
</head>
<body>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr bgcolor="#E5E5E5">
<td valign=center>
<a href="index.html">
<font color="#004faf">Home</font></a>
| <a href="classes.html">
<font color="#004faf">All Classes</font></a>
| <a href="mainclasses.html">
<font color="#004faf">Main Classes</font></a>
| <a href="annotated.html">
<font color="#004faf">Annotated</font></a>
| <a href="groups.html">
<font color="#004faf">Grouped Classes</font></a>
| <a href="functions.html">
<font color="#004faf">Functions</font></a>
</td>
<td align="right" valign="center"><img src="logo32.png" align="right" width="64" height="32" border="0"></td></tr></table><p align="right">[<a href="designer-manual-7.html">Prev: Creating Custom Widgets</a>] [<a href="designer-manual.html">Home</a>] [<a href="designer-manual-9.html">Next: Customizing and Integrating Qt Designer</a>]</p>
<h2 align="center">Creating Database Applications</h2>
<!-- index Databases --><!-- index SQL --><p>This chapter shows you how to use Qt's data-aware widgets from within <em>Qt Designer</em>. It demonstrates <tt>INSERT</tt>, <tt>UPDATE</tt> and <tt>DELETE</tt> in both <a href="qdatatable.html">QDataTable</a>s (tables) and <a href="qdatabrowser.html">QDataBrowser</a>s (forms). It also shows how to code Master-Detail relationships and Drilldown. A simple approach to foreign key handling is presented here; a more sophisticated approach is shown in the online SQL module documentation.</p>
<!-- index Databases!Drivers --><!-- index QODBC3, Database driver --><!-- index QOCI8, Database driver --><!-- index QPSQL7, Database driver --><!-- index QMYSQL3, Database driver --><!-- index Databases!Drivers!QODBC3 --><!-- index Databases!Drivers!QOCI8 --><!-- index Databases!Drivers!QPSQL7 --><!-- index Databases!Drivers!QMYSQL3 --><p>If you wish to run the examples or create your own applications using these widgets you need access to an SQL database and a Qt database driver that can connect to the database. At the time of writing the drivers that Qt supports are QODBC3 (Open Database Connectivity), QOCI8 (Oracle), QPSQL7 (PostgreSQL 6 and 7) and QMYSQL3 (MySQL).</p>
<!-- index Widgets!Data Aware --><!-- index Data Aware Widgets --><!-- index Databases!Data Aware Widgets --><p>Although you can use the Qt data-aware widgets to browse and edit data in SQL databases without having to write any SQL, a basic understanding of SQL is highly recommended. We assume that you have some familiarity with <tt>SELECT</tt>, <tt>INSERT</tt>, <tt>UPDATE</tt>, and <tt>DELETE</tt> statements. We also assume a basic understanding of the concepts of normalization and of primary and foreign keys. A standard text covering SQL databases is <em>An Introduction to Database Systems (7th ed.)</em> by C. J. Date, ISBN 0201385902.</p>
<!-- index In-place Editing --><!-- index Databases!In-place Editing --><p>In the following text we describe the creation of a 'book' database application. The application demonstrates how to use <a href="qdatatable.html">QDataTable</a>s including in-place record editing and how to set up master-detail relationships between <a href="qdatatable.html">QDataTable</a>s. It also explains how to drill down from a <a href="qdatatable.html">QDataTable</a> to another widget, for example, to a <a href="qdatabrowser.html">QDataBrowser</a> or a <a href="qdataview.html">QDataView</a> and how to perform record editing in a <a href="qdatabrowser.html">QDataBrowser</a>. A great deal of functionality is available from the classes directly in <em>Qt Designer</em> although subclassing is always available for finer control. If you want to build the 'book' examples you will need to create the example schema on your database.</p>
<p align="center"><img align="middle" src="book-main.png" width="502" height="532">
</p>
<blockquote><p align="center"><em>The Book Application</em></p></blockquote>
<blockquote>
<p align="center"><b> The Example Schema</b></p>
<p>Note that the examples in this chapter all use the tables, views and records which are defined in the <tt>qt/tools/designer/examples/book/book.sql</tt> file. This file has been tested with PostgreSQL 6 and PostgreSQL 7. You may need to modify the SQL in this file to recreate the example database on your own system.</p>
<p>Schema <tt>CREATE TABLE</tt> Statements</p>
<pre> CREATE TABLE author
( id integer primary key,
forename varchar(40),
surname varchar(40) );
</pre>
<pre> CREATE TABLE book
( id integer primary key,
title varchar(40),
price numeric(10,2),
authorid integer,
notes varchar(255) );
</pre>
<pre> CREATE TABLE sequence
( tablename varchar(10),
sequence numeric);
</pre>
<p>The 'book' table is simplified for the purposes of the example. It can only relate a book to a single author (authorid) and lacks an ISBN field. The 'sequence' table is used for generating unique index values for the example tables. Note that SQL databases often provide their own method for creating sequences (for example, using the <tt>CREATE SEQUENCE</tt> command) which is very likely to be a more optimal solution. For the sake of portability the examples will use a 'sequence' table which will work with the vast majority of SQL databases.</p>
</blockquote>
<h3><a name="1"></a>Setting Up Database Connections</h3>
<!-- index Databases!Connecting to Database Servers --><!-- index Connecting!Databases to Database Servers --><p>There are two aspects of database connections that we must consider. Firstly the connection we wish to use within <em>Qt Designer</em> itself, and secondly the connection we wish to use in the applications that we create.</p>
<h4><a name="1-1"></a>Setting Up Qt Designer's Connections</h4>
<p align="center"><img align="middle" src="databaseconnections.png" width="479" height="352">
</p>
<blockquote><p align="center"><em>Edit Database Connections Dialog</em></p></blockquote>
<p>Choose <b>Project|Database Connections</b> from the menu bar. The <em>Edit Database Connections</em> dialog will appear. Click <b>New Connection</b>. For applications that use a single database it will probably be most convenient to use the default connection name of '(default)'. If you use more than one database then each one must be given a unique name. A driver must be chosen from the Driver combo box. The database name may be available in the Database Name combo box or may have to be typed in. The database name, username, password, hostname and port should be provided by your database system administrator. When the Connection information has been completed click <b>Connect</b>. If the connection is made the connection name will appear in the list box on the left hand side of the dialog. You can now close the dialog; the connection settings will remain in effect until you change or delete them or exit from <em>Qt Designer</em>.</p>
<p><b>Warning:</b> If you are using an existing SQLite database, ensure that the name you specify in the "Database Name" field is not the same as the existing database file. <em>Qt Designer</em> will create a configuration file using the name given for the database and will overwrite any existing files with the same name.</p>
<!-- index Projects!Database Connections --><p><em>Qt Designer</em> can remember database connection settings in <tt>qmake</tt> project files. Create a new project, e.g. click <b>File|New</b>, then click the 'C++ Project' icon to invoke the <em>Project Settings</em> dialog. Click the ellipsis button to invoke the <em>Save As</em> dialog; navigate to the project's directory (creating it if necessary). Make sure you're in the project's directory, then enter a project name of 'book.pro'. Click the <b>Save</b> button to return to the <em>Project Settings</em> dialog, then click <b>OK</b>. Next time you start <em>Qt Designer</em> instead of opening individual <tt>.ui</tt> files open the <tt>.pro</tt> project file instead and <em>Qt Designer</em> will automatically reload the project's connection settings. To activate the connection click <b>Project|Database Connections</b>. The connections previously saved with the project will be listed in the left hand list box. Click the connection you wish to use and then click <b>Connect</b>. This connection will be used from now on, e.g. for previewing <a href="qdatatable.html">QDataTable</a>s. Opening a project file also causes <em>Qt Designer</em> to load in the list of forms associated with the project into the Project Overview window. In most of the explanation that follows we will assume that you use project files and have clicked <b>Connect</b> so that there is always a connection available when you work in <em>Qt Designer</em>.</p>
<h4><a name="1-2"></a>Setting Up Connections for Applications</h4>
<p>The applications you create must make their own connections to the SQL database. We provide an example function, <tt>createConnections()</tt>, that you can use as a basis for your own code.<!-- index createConnections() --></p>
<pre>
bool createConnections()
{
// create the default database connection
QSqlDatabase *defaultDB = QSqlDatabase::addDatabase( "QPSQL7" );
if ( ! defaultDB ) {
qWarning( "Failed to connect to driver" );
return FALSE;
}
defaultDB->setDatabaseName( "book" );
defaultDB->setUserName( "bookuser" );
defaultDB->setPassword( "bookpw" );
defaultDB->setHostName( "bookhost" );
if ( ! defaultDB->open() ) {
qWarning( "Failed to open books database: " +
defaultDB->lastError().driverText() );
qWarning( defaultDB->lastError().databaseText() );
return FALSE;
}
return TRUE;
}
</pre>
<!-- index addDatabase() --><p>We call <tt>addDatabase()</tt> passing it the name of the driver we wish to use. We then set the connection information by calling the <tt>set</tt>... functions. Finally we attempt to open the connection. If we succeed we return TRUE, otherwise we output some error information and return FALSE. From <tt>qt/tools/designer/examples/book/book1/main.cpp</tt></p>
<pre> int main( int argc, char *argv[] )
{
<a href="qapplication.html">QApplication</a> app( argc, argv );
if ( ! createConnections() )
return 1;
BookForm bookForm;
app.<a href="qapplication.html#setMainWidget">setMainWidget</a>( &bookForm );
bookForm.show();
return app.<a href="qapplication.html#exec">exec</a>();
}
</pre>
<!-- index Databases!Connecting to Multiple Database Servers --><!-- index createConnections() --><!-- index main.cpp --><!-- index addDatabase() --> <p>All the examples presented in this chapter call <tt>createConnections()</tt> after creating the <a href="qapplication.html">QApplication</a> object in their <tt>main.cpp</tt> file and make use of the default connection. If you need to connect to multiple databases use the two-argument form of <tt>addDatabase()</tt>, passing it both the name of the driver and a unique identifier. This is explained further in the <a href="http://doc.trolltech.com/sql.html">Qt SQL Module documentation</a>.</p>
<!-- index QSqlDatabase::database() --><p>You do not need to keep a reference to database connections. If you use a single database connection, this becomes the default connection and database functions will use this connection automatically. We can always get a pointer to any of our connections by calling <tt>QSqlDatabase::database()</tt>.</p>
<p>If you create a <tt>main.cpp</tt> file using <em>Qt Designer</em>, this file will <em>not</em> include <tt>createConnections()</tt>. We do not include this function because it needs the username and password for the database connection, and you may prefer to handle these differently from our simple example function. As a result, applications that preview correctly in <em>Qt Designer</em> will not run unless you implement your own database connections function.</p>
<h3><a name="2"></a>Using <a href="qdatatable.html">QDataTable</a></h3>
<p><a href="qdatatable.html">QDataTable</a>s may be placed on any form to provide browsing of database tables and views. <a href="qdatatable.html">QDataTable</a>s can also be used to update or delete records in-place, i.e. inside the cells themselves. Inserting records via a <a href="qdatatable.html">QDataTable</a> usually requires connecting to the<!-- index primeInsert() --> <tt>primeInsert()</tt> signal, so that we can generate primary keys for example, or provide default values. If we wish to present records using a form view (perhaps combining data from several tables and views) we might use several <a href="qdatabrowser.html">QDataBrowser</a>s and <a href="qdataview.html">QDataView</a>s.</p>
<h4><a name="2-1"></a>Quickly Viewing a Database Table</h4>
<p>This example, along with all the other examples in this chapter, has the project name 'book' and uses the database created by the<!-- index book.sql --> <tt>book.sql</tt> script. As we work through the chapter we will build the 'book' application step by step. Create or copy the <tt>qt/tools/designer/examples/book/book1/main.cpp</tt> file shown earlier. The project file for this first example is <tt>qt/tools/designer/examples/book/book1/book.pro</tt>. Start a new project by clicking <b>File|New</b>, then click the 'C++ Project' icon to invoke the <em>Project Settings</em> dialog. Click the ellipsis button to invoke the <em>Save As</em> dialog; navigate to the project's directory (creating it if necessary). Make sure you're in the project's directory, then enter a project name of 'book.pro'. Click the <b>Save</b> button to return to the <em>Project Settings</em> dialog, then click <b>OK</b>. Now click <b>Project|Database Connections</b>. Fill in the connection information appropriate to your database then press <b>Connect</b>. The connection name should now appear in the left hand list box. (If this doesn't happen you'll need to contact your database systems administrator for help.) Close the dialog.</p>
<p>We will now create a new form with a <a href="qdatatable.html">QDataTable</a> that's connected to one of our database tables.</p>
<p>Click <b>File|New</b>. The <em>New File</em> dialog presents us with a number of form templates to choose from. Choose the 'Dialog' form and click <b>OK</b>. Now click <b>File|Save</b>. You will be prompted for a filename, call it <tt>book.ui</tt>.</p>
<h5><a name="2-1-1"></a>Setting up a <a href="qdatatable.html">QDataTable</a></h5>
<!-- index Databases!SQL Table Wizard --><!-- index Wizards!SQL Table --><p>To place a <a href="qdatatable.html">QDataTable</a> widget on the form either click <b>Tools|Views|DataTable</b> or click the <b>DataTable</b> toolbar button. Click on the form and the Data Table Wizard will appear.</p>
<ol type=1><li><p>The <em>Database Connection and Table</em> wizard page is used to set up a connection if one doesn't exist and to choose the table or view for the <a href="qdatatable.html">QDataTable</a>. (See <a href="designer-manual-8.html#1-1">Setting Up Qt Designer's Connections</a>.)</p>
<p>Click the connection you wish to use, listed in the left hand Database Connection list box, e.g. "(default)". The available tables and views will appear in the right hand Table list box. Click the 'author' table and then click the <b>Next</b> button.</p>
<li><p>The <em>Displayed Fields</em> wizard page provides a means of selecting which fields should be displayed in the <a href="qdatatable.html">QDataTable</a> and in what order. By default all fields except the primary key (if there is one) are in the Displayed Fields list box. The left- and right-pointing blue arrow buttons can be used to move fields between the Displayed Fields and the Available Fields list boxes. The blue up and down pointing arrow buttons are used to select the display order of the displayed fields.</p>
<p>The default settings are the ones we want so simply click <b>Next</b>.</p>
<li><!-- index Properties!DataTable --><p>The <em>Table Properties</em> wizard page provides convenient access to some of the database-related properties of the <a href="qdatatable.html">QDataTable</a>.</p>
<p>Make sure the Confirm Deletes checkbox is checked, then click <b>Next</b>.</p>
<li><p>The <em>SQL</em> wizard page is used to set the <a href="qdatatable.html">QDataTable</a>'s Filter and Sort properties. The Filter is an SQL <tt>WHERE</tt> clause (without the word 'WHERE'). For example, to only list authors whose surnames begin with 'P', we would enter <tt>title LIKE 'P%'</tt>. We'll leave the filter empty. The Available Fields list box lists all the fields. The Sort By list box lists the fields that the <a href="qdatatable.html">QDataTable</a> is to sort by and the direction of their sorting (ASCending or DESCending). The left and right blue arrows are used to move fields between the two list boxes. The up and down blue arrows move fields up and down within the Sort By list box. The ASC or DESC setting is changed with the 'sort order' toolbar button.</p>
<p>Move the surname and forename fields into the Sort By list box and click <b>Next</b>.</p>
<li><p>The <em>Finish</em> wizard page gives us the opportunity to go back and change any of our settings. We will be able to change them later through the <a href="qdatatable.html">QDataTable</a>'s properties so we can finish with the wizard.</p>
<p>Click <b>Finish</b>.</p>
</ol><p>The table will appear on the form with each column labelled with a default column name. If you wish to change the settings then most of them are available in the property window. The display names, the fields they are based upon, and the order of appearance of the columns can be changed using the <em>Edit Table</em> dialog (explained later) by right clicking the <a href="qdatatable.html">QDataTable</a> and left clicking <b>Edit</b>.</p>
<h5><a name="2-1-2"></a>Laying out the Form</h5>
<p>Click on the form and click the <b>Lay Out Vertically</b> toolbar button. Now click <b>Preview|Preview Form</b>; the form will run and the table will automatically display all the records.</p>
<p>To turn the form we've created into an executable application we must add the<!-- index main.cpp --> <tt>main.cpp</tt> file to the project file and make the project. We should also do some renaming to make things easier to understand.</p>
<ol type=1><li><p>Click on the form and change its name to 'BookForm' and its caption to 'Book'. Click on the <a href="qdatatable.html">QDataTable</a> and change its name to 'AuthorDataTable'.</p>
<li><p>Click <b>File|Save All</b>.</p>
<li><p>Open the project file, e.g.<!-- index book.pro --> <tt>book.pro</tt>, in a plain text editor and add the line: <tt>SOURCES += main.cpp</tt> at the end of the file.</p>
<li><p>Run <tt>qmake</tt> to generate the make file, e.g. <tt>qmake -o Makefile book.pro</tt>, then make and run the <tt>book</tt> program.</p>
</ol><p>This example shows how easy it is to use <a href="qdatatable.html">QDataTable</a> to show the contents of a database table or view. You can use the application we've just built to update and delete author records. In the examples that follow we will cover insertions, setting up master-detail relationships, drilldown and foreign key lookups.</p>
<blockquote>
<p align="center"><b> A Note on Foreign Keys</b></p>
<!-- index Foreign Keys --><!-- index Databases!Foreign Keys --><p>In most relational databases tables contain fields which are foreign keys into other tables. In our 'book' database example the authorid in the book table is a foreign key into the author table. When we present a form to the end user we do not usually want the foreign key itself to be visible but rather the text associated with it. Thus, we would want the author's name to appear rather than the author id when we show book information. In many databases, this can be achieved by using a view. See your database's documentation for details.</p>
</blockquote>
<h4><a name="2-2"></a>Inserting Records in <a href="qdatatable.html">QDataTable</a>s</h4>
<!-- index Inserting Records --><!-- index Databases!Inserting Records --><p>Record insertion into a relational database usually requires the generation of a primary key value which uniquely identifies the record in the table. Also we often want to create default values for some fields to minimize the user's work. We will create a slot to capture the <a href="qdatatable.html">QDataTable</a>s<!-- index primeInsert() --> <tt>primeInsert()</tt> signal and populate the <a href="qsqlrecord.html">QSqlRecord</a> insertion buffer with a unique primary key.</p>
<ol type=1><li><p>Click <b>Edit|Slots</b> to invoke the <em>Edit Functions</em> dialog. Click <b>New Function</b>, then enter the slot name <tt>primeInsertAuthor(QSqlRecord*)</tt> into the Function Properties' Function line edit box. Click <b>OK</b>.</p>
<li><p>Click the <b>Connect Signals/Slots</b> toolbar button, then click the AuthorDataTable, drag to the form and release the mouse. The <em>Edit Connections</em> dialog will now appear. Click the<!-- index primeInsert() --> <tt>primeInsert()</tt> signal and then the <tt>primeInsertAuthor()</tt> slot to make the connection. Now click <b>OK</b>.</p>
<li><p>Click the Members tab of the Object Explorer window (click <b>Window|Views|Object Explorer</b> to make the window visible if necessary). Click the <tt>primeInsertAuthor()</tt> slot and an editor window will appear.</p>
<li><p>We must change the <tt>BookForm::primeInsertAuthor()</tt> slot to specify the parameter name and perform the necessary action:</p>
<pre> void BookForm::primeInsertAuthor( <a href="qsqlrecord.html">QSqlRecord</a> * buffer )
{
<a href="qsqlquery.html">QSqlQuery</a> query;
query.<a href="qsqlquery.html#exec">exec</a>( "UPDATE sequence SET sequence = sequence + 1 WHERE tablename='author';" );
query.<a href="qsqlquery.html#exec">exec</a>( "SELECT sequence FROM sequence WHERE tablename='author';" );
if ( query.<a href="qsqlquery.html#next">next</a>() ) {
buffer-><a href="qsqlrecord.html#setValue">setValue</a>( "id", query.<a href="qsqlquery.html#value">value</a>( 0 ) );
}
}
</pre>
<p>A <a href="qsqlquery.html">QSqlQuery</a> object is used to increment and retrieve a unique 'sequence' number for the author table. The signal passed us a pointer to the insertion buffer and we then put the value we've retrieved, i.e. the next sequence number, into the buffer's id field. (Again, note that SQL databases often support a native 'sequence' function. The method used here is inappropriate for production systems, and is for example purposes only. See your database's documentation for details on how to generate unique keys in code. In many cases, the database can generate them automatically, or the database may provide a special syntax for dealing with sequences.)</p>
</ol><!-- index Deleting!Records!Databases --><!-- index Databases!Deleting Records --><!-- index Updating Records --><!-- index Databases!Updating Records --><p>If we rebuild the application it will now support <tt>INSERT</tt> as well as <tt>UPDATE</tt> and <tt>DELETE</tt>. We could easily have added additional code to insert default values, e.g. today's date into a date field, if necessary.</p>
<!-- index Databases!Browsing --><!-- index Databases!Confirmations --><!-- index Browsing Databases --><p>Browsing is supported by clicking records and by using the arrow keys. Once a record is active (highlighted) we can edit the it. Press the <b>Insert</b> key to <tt>INSERT</tt> a new record; press <b>F2</b> to <tt>UPDATE</tt> the current record; press the <b>Del</b> key to <tt>DELETE</tt> the current record. All these operations take place immediately. Users can be given the opportunity to confirm their edits by setting the <a href="qdatatable.html">QDataTable</a>'s confirmEdits property to True. If the confirmEdits property is True then user confirmation will be required for all insertions, updates and deletes. For finer control you can set the confirmInsert, confirmUpdate and confirmDelete properties individually.</p>
<blockquote>
<p align="center"><b> <a href="qdatatable.html">QDataTable</a> User Interface Interaction</b></p>
<!-- index Databases!User Interface Interaction --><!-- index User Interface Interaction, Databases --><p>The default user-interface behavior for <a href="qdatatable.html">QDataTable</a>s is as follows:</p>
<ul><li><p>Users can move to records by clicking the scrollbar and clicking records with the mouse. They can also use the keyboard's navigation keys, e.g. <b>Left Arrow</b>, <b>Right Arrow</b>, <b>Up Arrow</b>, <b>Down Arrow</b>, <b>Page Up</b>, <b>Page Down</b>, <b>Home</b> and <b>End</b>.</p>
<li><p><tt>INSERT</tt> is initiated by right-clicking the record and clicking Insert or by pressing the <b>Ins</b> (Insert) key. The user moves between fields using <b>Tab</b> and <b>Shift+Tab</b>. The <tt>INSERT</tt> will take place if the user presses <b>Enter</b> or <b>Tab</b>s off the last field. If autoEdit is TRUE the insert will take place if the user navigates to another record. <tt>INSERT</tt> is cancelled by pressing <b>Esc</b> (Escape). If autoEdit is FALSE navigating to another record also cancels the <tt>INSERT</tt>. Setting confirmInsert to TRUE will require the user to confirm each <tt>INSERT</tt>.</p>
<li><p><tt>UPDATE</tt> is initiated by right-clicking the record and clicking Update or by pressing <b>F2</b>. The update will take place if the user presses Enter or Tabs off the last field. If autoEdit is TRUE the update will take place if the user navigates to another record. <tt>UPDATE</tt> is cancelled by pressing <b>Esc</b>. If autoEdit is FALSE navigating to another record also cancels the <tt>UPDATE</tt>. Setting confirmUpdate to TRUE will require the user to confirm each <tt>UPDATE</tt>.</p>
<li><p><tt>DELETE</tt> is achieved by right-clicking the record and clicking Delete or by pressing the <b>Del</b> (Delete) key. Setting confirmDelete to TRUE will require the user to confirm each <tt>DELETE</tt>.</p>
</ul><p>You can change this default behavior programmatically if required.</p>
</blockquote>
<h4><a name="2-3"></a>Relating Two Tables Together (Master-Detail)</h4>
<!-- index Databases!Master-Detail Relationships --><!-- index Master-Detail Relationships --><p>Databases often have pairs of tables that are related. For example, an invoice table might list the numbers, dates and customers for invoices, but not the actual invoice items, which an invoice item table might store. In the 'book' application we wish to have a <a href="qdatatable.html">QDataTable</a> that we can use to browse through the authors table and a second <a href="qdatatable.html">QDataTable</a> to show the books they've written.</p>
<p>Open the book project if it isn't already open <em>Qt Designer</em>. We will modify this project to show two <a href="qdatatable.html">QDataTable</a>s that relate the author table to the book table.</p>
<ol type=1><li><p>Click the author <a href="qdatatable.html">QDataTable</a> and then click the <b>Break Layout</b> toolbutton.</p>
<li><p>Resize the DataTable so that it only occupies the top half of the form.</p>
<li><p>Now click on the DataTable toolbutton and click on the bottom half of the form. The Table Wizard will appear. (This Wizard is explained in <a href="designer-manual-8.html#2-1">Quickly Viewing a Database Table</a>.)</p>
<ol type=1><li><p>Click the connection you're using and click the book table. Click the <b>Next</b> button.</p>
<li><p>Since we do not want them visible, make sure the authorid and id fields are moved to the Available Fields list box by using the arrow buttons. Move the title field to the top of the Displayed Fields, and move the price field above the notes field. Click the <b>Next</b> button.</p>
<li><p>On the Table Properties page click the Read Only checkbox then click the <b>Next</b> button.</p>
<li><p>On the SQL page we will leave the Filter (<tt>WHERE</tt> clause) empty. Move the title field to the Sort By list box and click <b>Next</b>. Now click <b>Finish</b>.</p>
<li><p>Change this <a href="qdatatable.html">QDataTable</a>'s name to "BookDataTable".</p>
</ol><li><p><b>Shift+Click</b> the top <a href="qdatatable.html">QDataTable</a> so that both <a href="qdatatable.html">QDataTable</a>s are selected and then click the <b>Lay Out Vertically (in Splitter)</b> toolbar button.</p>
<li><p>Click on the form and click the <b>Lay Out Vertically</b> toolbar button.</p>
</ol><p>Preview the form by clicking <b>Preview|Preview Form</b>. All the authors are displayed in the top <a href="qdatatable.html">QDataTable</a> and all the books are displayed in the bottom <a href="qdatatable.html">QDataTable</a>. However we only want the books of the currently selected author showing in the bottom <a href="qdatatable.html">QDataTable</a>. We will deal with this by filtering the records in the book table according to the author selected in the author table.</p>
<blockquote>
<p align="center"><b> Using the Table Editor</b></p>
<!-- index Value Editors!SQL Table Editor --><p align="center"><img align="middle" src="edit-dbtable-dialog.png" width="613" height="405">
</p>
<blockquote><p align="center"><em>Edit Table Dialog</em></p></blockquote>
<p><a href="qdatatable.html">QDataTable</a>s are created and set up using the SQL Table Wizard. Like any other <em>Qt Designer</em> widget their properties may be changed in the Properties window. Some of the column and row based properties can also be be changed using the <em>Edit Table</em> dialog. This dialog is invoked by right clicking the <a href="qdatatable.html">QDataTable</a> and left clicking the <b>Edit</b> menu item. The right hand half of the <em>Edit Table</em> dialog is where we choose the fields we wish to display, their order and their labels. The procedure for creating columns is as follows:</p>
<ol type=1><li><p>Click the <b>New Column</b> button.</p>
<li><p>Drop down the Field combobox to list the available fields.</p>
<li><p>Click the field you wish to include at this point.</p>
<li><p><em>Optionally</em> edit the Label if the default isn't appropriate.</p>
<li><p><em>Optionally</em> click the Pixmap ellipsis (<b>...</b>) button to choose a pixmap to be displayed to the left of the column's label. (The ellipsis button appears when you click in the Value part of the Properties list by a <em>pixmap</em> or <em>iconSet</em> property.)</p>
</ol><p>Repeat the steps listed above for each column you wish to add. Once all the fields have been added you can change their ordering by using the blue up and down arrow buttons. At any point you can press <b>Apply</b> to see how the table will look. Finally click the <b>OK</b> button to save the properties you have set. You can always return to the table editor to change these settings later.</p>
</blockquote>
<h5><a name="2-3-1"></a>Filtering One <a href="qdatatable.html">QDataTable</a> by Another</h5>
<!-- index Databases!Relating Tables --><p>To filter the book table's records we need to capture the author <a href="qdatatable.html">QDataTable</a>'s<!-- index currentChanged() --> <tt>currentChanged()</tt> signal and change the BookDataTable's filter accordingly.</p>
<ol type=1><li><p>Click <b>Edit|Slots</b>. In the <em>Edit Functions</em> dialog click <b>New Function</b> and enter a slot name of <tt>newCurrentAuthor(QSqlRecord*)</tt>. Click <b>OK</b>.</p>
<li><p>Click <b>Edit|Connections</b> to invoke the <em>View and Edit Connections</em> dialog. Create a new connection, connecting the AuthorDataTable's currentChanged() signal to the form's newCurrentAuthor() slot. Click <b>OK</b>.</p>
<li><p>Click the Members tab of the Object Explorer window (click <b>Window|Views|Object Explorer</b> to make the window visible if necessary). Click the <tt>newCurrentAuthor()</tt> slot and an editor window will appear.</p>
<li><p>We must change the <tt>BookForm::newCurrentAuthor()</tt> slot to specify the parameter name and perform the necessary action:</p>
<pre> void BookForm::newCurrentAuthor( <a href="qsqlrecord.html">QSqlRecord</a> *author )
{
BookDataTable->setFilter( "authorid=" + author-><a href="qsqlrecord.html#value">value</a>( "id" ).toString() );
BookDataTable->refresh();
}
</pre>
<p>All that's required now is to change the BookDataTable's filter and refresh the <a href="qdatatable.html">QDataTable</a> to show the results of the filter.</p>
</ol><h5><a name="2-3-2"></a>Preparing the Interface for Drilldown</h5>
<!-- index Databases!Drilldown --><!-- index Drilldown --><p>We can now browse and edit authors and see their books in the BookDataTable. In the next section we explore <a href="qdatabrowser.html">QDataBrowser</a>, which will allow us to drill down to a dialog through which we can edit books. For now we will add some buttons to the main BookForm which we will use to invoke the book editing dialog.</p>
<ol type=1><li><p>Click the form, then click the <b>Break Layout</b> toolbar button. Resize the form to make room for some buttons at the bottom.</p>
<li><p>Add two buttons to the bottom of the form. Change their names and labels to the following:</p>
<ul><li><p>EditPushButton -- &Edit Books</p>
<li><p>QuitPushButton -- &Quit</p>
</ul><p>Hold down the Shift key and Click both buttons (i.e. <b>Shift+Click</b> the buttons) and click the <b>Lay Out Horizontally</b> toolbar button. Click the form and click the <b>Lay Out Vertically</b> toolbar button.</p>
<li><p>We will provide the Quit button with functionality now and work on the rest shortly. Click <b>Edit|Connections</b>, then connect the the Quit button's clicked() signal to the form's accept() slot. Click <b>OK</b>.</p>
</ol><h3><a name="3"></a>Using <a href="qdatabrowser.html">QDataBrowser</a> and <a href="qdataview.html">QDataView</a></h3>
<p align="center"><img align="middle" src="book-dialog.png" width="548" height="405">
</p>
<blockquote><p align="center"><em>The Book Application's Edit Books Dialog</em></p></blockquote>
<h4><a name="3-1"></a>Drilling Down to a Form using <a href="qdatabrowser.html">QDataBrowser</a></h4>
<h5><a name="3-1-1"></a>Setting up a <a href="qdatabrowser.html">QDataBrowser</a></h5>
<!-- index Databases!Drilldown --><!-- index Drilldown --><!-- index Databases!Data Browser Wizard --><!-- index Wizards!Data Browser --><p>We will now create a new form to allow users to edit book records. Click the <b>New</b> toolbar button, click the Dialog template from the <em>New File</em> dialog and click <b>OK</b>. Change the name of the form to EditBookForm and its caption to 'Edit Books'. Click the <b>Save</b> toolbar button and call the file <tt>editbook.ui</tt>. Now that we have the form we can add a <a href="qdatabrowser.html">QDataBrowser</a> to show the book records.</p>
<ol type=1><li><p>Click the <b>Data Browser</b> toolbar button, then click the form. The Data Browser Wizard will appear.</p>
<li><p>The <em>Database Connection and Table</em> wizard page is used to set up a connection if one doesn't exist and to choose the table or view for the <a href="qdatabrowser.html">QDataBrowser</a>. (See <a href="designer-manual-8.html#1-1">Setting Up Qt Designer's Connections</a>.)</p>
<p>Click the connection you wish to use, listed in the Database Connection list box, e.g. "(default)". The available tables and views will appear in the Table list box. Click the book table and then click the <b>Next</b> button.</p>
<li><p>The <em>Displayed Fields</em> wizard page provides a means of selecting which fields should be displayed in the <a href="qdatabrowser.html">QDataBrowser</a> and in what order. By default all fields except the primary key (if there is one) are in the right hand Displayed Fields list box. The left and right blue arrow buttons can be used to move fields between the Displayed Fields and the Available Fields list boxes. The blue up and down arrow buttons are used to select the display order of the displayed fields.</p>
<p>We don't want to see the authorid foreign key field on the form, so move it to the Available Fields list box. Also, move the title field to the top of the Displayed Fields list. Click the <b>Next</b> button.</p>
<li><p>The <em>Navigation and Editing</em> wizard page allows us to choose which navigation and editing buttons should appear on the form.</p>
<p>We will accept the defaults and simply click the <b>Next</b> button.</p>
<li><p>The <em>SQL</em> wizard page is used to set the <a href="qdatabrowser.html">QDataBrowser</a>'s Filter and Sort properties. The Filter is an SQL <tt>WHERE</tt> clause (without the word 'WHERE'). For example, to only list books that cost less than 50 (of some currency, e.g. dollars), we would enter <tt>price < 50</tt>. We will leave the filter empty. The Available Fields list box lists all the fields. The Sort By list box lists the fields that the <a href="qdatabrowser.html">QDataBrowser</a> is to sort by and the direction of their sorting (ASCending or DESCending). The left and right blue arrows are used to move fields between the two list boxes. The up and down blue arrows move fields up and down within the Sort By list box. The ASC or DESC setting is changed with the sort order button.</p>
<p>Move the title field into the Sort By list box and click <b>Next</b>.</p>
<li><p>The <em>Layout</em> wizard page is used to specify the initial layout of the form.</p>
<p>Change the Number of Columns to 1, then click <b>Next</b>. Now click <b>Finish</b>.</p>
<li><p>The <a href="qdatabrowser.html">QDataBrowser</a> will now appear on the form. Resize the form to make it smaller. Click the <a href="qdatabrowser.html">QDataBrowser</a> then click the <b>Break Layout</b> toolbar button. Click the buttons then click the <b>Break Layout</b> toolbar button. Add another button called 'PushButtonClose' with the text '&Close' and place it to the right of the Delete button.</p>
<li><p><b>Shift+Click</b> the Insert, Update, Delete and Close buttons, then click the <b>Lay Out Horizontally</b> toolbar button. Click the <a href="qdatabrowser.html">QDataBrowser</a>, then click the <b>Lay Out in a Grid</b> toolbar button. Finally click the form and click the <b>Lay Out Vertically</b> toolbar button. Now click the <a href="qdatabrowser.html">QDataBrowser</a> and rename it 'BookDataBrowser'.</p>
<li><p><em>Qt Designer</em> will generate the necessary code to make the browser operational (including generating the appropriate cursor, sort and filter code).</p>
<p>For finer control over the form, we will be creating our own database cursor. Therefore, set the BookDataBrowser's frameworkCode property to FALSE in the Properties window to prevent <em>Qt Designer</em> from generating redundant code for the cursor.</p>
</ol><blockquote>
<p align="center"><b> <a href="qdatabrowser.html">QDataBrowser</a> User Interface Interaction</b></p>
<p>The user-interface behavior for <a href="qdatabrowser.html">QDataBrowser</a>s is created by connecting slots and signals. The slots provided are:</p>
<ul><li><!-- index insert() --><p><tt>insert()</tt>,<!-- index update() --> <tt>update()</tt> and<!-- index del() --> <tt>del()</tt> for editing;</p>
<li><!-- index first() --><p><tt>first()</tt>,<!-- index next() --> <tt>next()</tt>,<!-- index prev() --> <tt>prev()</tt>, and<!-- index last() --> <tt>last()</tt> for navigation;</p>
<li><!-- index refresh() --><p><tt>refresh()</tt> to refresh the cursor from the database;</p>
<li><!-- index readFields() --><p><tt>readFields()</tt> to read data from the cursor's edit buffer and<!-- index writeFields() --> <tt>writeFields()</tt> to write the form's data to the cursor's edit buffer;</p>
<li><!-- index clearValues() --><p><tt>clearValues()</tt> to clear the form's values.</p>
</ul><p>If you use <em>Qt Designer</em>'s <a href="qdatabrowser.html">QDataBrowser</a> wizard you will be given the option of creating a default set of buttons for navigation and editing. The behavior of these buttons is set up using the slots described above to provide the following functionality:</p>
<ul><li><p><tt>INSERT</tt> is initiated by pressing the <b>Ins</b> (Insert) key. The user moves between fields using <b>Tab</b> and <b>Shift+Tab</b>. If the user presses the Update button the <tt>INSERT</tt> will take place and the user will be taken to the record they have just inserted. If the user presses the Insert button (i.e. a second time) the <tt>INSERT</tt> will take place and a new insertion will be initiated. If autoEdit is TRUE the <tt>INSERT</tt> will take place if the user navigates to another record. <tt>INSERT</tt> is cancelled by pressing the <b>Esc</b> key or by pressing the <b>Del</b> (Delete) key. If autoEdit is FALSE then navigating to another record also cancels the <tt>INSERT</tt>. Setting confirmInsert to TRUE will require the user to confirm each <tt>INSERT</tt>.</p>
<li><p><tt>UPDATE</tt> is automatically initiated whenever the user navigates to a record. An update will take place if the user presses the Update button. If autoEdit is TRUE the update will take place if the user navigates to another record. <tt>UPDATE</tt> is cancelled by pressing the <b>Esc</b> key or by pressing the <b>Del</b> button. If autoEdit is FALSE then navigating to another record also cancels the <tt>UPDATE</tt>. Setting confirmUpdate to TRUE will require the user to confirm each <tt>UPDATE</tt>.</p>
<li><p><tt>DELETE</tt> is achieved by pressing the <b>Del</b> key. Setting confirmDelete to TRUE will require the user to confirm each <tt>DELETE</tt>.</p>
</ul></blockquote>
<h5><a name="3-1-2"></a>Performing the Drilldown</h5>
<!-- index Databases!Drilldown --><!-- index Drilldown --><p>We now have a working form for editing book records. We need to start the form when the user clicks our 'Edit Books' button, and to navigate to the record they have selected in the BookDataTable. We also need to provide a means of editing the foreign keys, e.g. authorid.</p>
<ol type=1><li><p>We need to make a new slot to connect the Edit Books' button's<!-- index clicked() --> <tt>clicked()</tt> signal to. Click on the Book form to make it <em>Qt Designer</em>'s active form. Invoke the <em>Edit Functions</em> dialog and create a new function called <tt>editClicked()</tt>. Now click <b>Edit|Connections</b>. Connect the Edit Books button's clicked() signal to the form's editClicked() slot. Clicked() slot. Click <b>OK</b> to leave the dialog.</p>
<!-- index Object Hierarchy --><li><p>In the Object Explorer window click Members and then click the <tt>editClicked</tt> function. We need to change it to the following:</p>
<pre> void BookForm::editClicked()
{
EditBookForm *dialog = new EditBookForm( this, "Edit Book Form", TRUE );
<a href="qsqlcursor.html">QSqlCursor</a> cur( "book" );
dialog->BookDataBrowser->setSqlCursor( &cur );
dialog->BookDataBrowser->setFilter( BookDataTable->filter() );
dialog->BookDataBrowser->setSort(QSqlIndex::<a href="qsqlindex.html#fromStringList">fromStringList</a>(
BookDataTable->sort(), &cur ) );
dialog->BookDataBrowser->refresh();
int i = BookDataTable->currentRow();
if ( i == -1 ) i = 0; // Always use the first row
dialog->BookDataBrowser->seek( i );
dialog->exec();
delete dialog;
BookDataTable->refresh();
}
</pre>
<p>We create our dialog as before. We also create a cursor over the book table and set the dialog's <a href="qdatabrowser.html">QDataBrowser</a>, BookDataBrowser, to use this new cursor. We set the <a href="qdatabrowser.html">QDataBrowser</a>'s filter and sort to those that applied to the main form's book <a href="qdatatable.html">QDataTable</a>. We refresh the <a href="qdatabrowser.html">QDataBrowser</a> and seek to the same record the user was viewing on the main form. Then we exec the dialog and delete it when the user has finished with it. Finally we update the BookDataTable in the main form to reflect any changes that were made in the dialog.</p>
<li><p>Because our code refers to a class declared in <tt>editbook.h</tt> and to a <a href="qdatabrowser.html">QDataBrowser</a> we need to add two additional include files. Click on the BookForm, then click on the Members tab of the Object Explorer window. Right click the 'Includes (In Declaration)' item and click New. Type in <tt>"editbook.h"</tt>. Now add a second include, this time, <tt></tt><qdatabrowser.h>.</p>
</ol><p>Now when we navigate through the author and book records in the BookForm we can click the Edit Books button to launch our Edit Books dialog. Although the dialog supports <tt>UPDATE</tt>, <tt>DELETE</tt> and navigation over the book table, we cannot edit the foreign keys nor perform inserts. We will deal with insertion in the same way as we did with the <a href="qdatatable.html">QDataTable</a>, then we will handle the foreign key relationship to author.</p>
<h5><a name="3-1-3"></a>Inserting into a <a href="qdatabrowser.html">QDataBrowser</a></h5>
<p>We will create a slot to receive the Edit Books form's<!-- index primeInsert() --> <tt>primeInsert()</tt> signal so that we can insert a unique primary key.</p>
<ol type=1><li><p>Click on the Edit Books form, then create a new Slot called <tt>primeInsertBook(QSqlRecord*)</tt>.</p>
<p>Click <b>Edit|Slots</b>, then click the <b>New Function</b> button and type the new function name in the Function Properties Function edit box. Click <b>OK</b>.</p>
<li><p>Connect the BookDataBrowser's<!-- index primeInsert() --> <tt>primeInsert()</tt> signal to the <tt>primeInsertBook()</tt> slot.</p>
<p>Click the <b>Connect Signals/Slots</b> toolbar button, then click the BookDataBrowser and drag to the form; release the mouse on the form. Now click the<!-- index primeInsert() --> <tt>primeInsert()</tt> signal and the primeInsertBook slot. Click <b>OK</b>.</p>
<li><p>In the Object Explorer window click Members and then click the <tt>primeInsertBook</tt> slot. We need to change it to the following:</p>
<pre> void EditBookForm::primeInsertBook( <a href="qsqlrecord.html">QSqlRecord</a> * buffer )
{
<a href="qsqlquery.html">QSqlQuery</a> query;
query.<a href="qsqlquery.html#exec">exec</a>( "UPDATE sequence SET sequence = sequence + 1 WHERE tablename='book';" );
query.<a href="qsqlquery.html#exec">exec</a>( "SELECT sequence FROM sequence WHERE tablename='book';" );
if ( query.<a href="qsqlquery.html#next">next</a>() ) {
buffer-><a href="qsqlrecord.html#setValue">setValue</a>( "id", query.<a href="qsqlquery.html#value">value</a>( 0 ) );
}
}
</pre>
<li><!-- index clicked() --><!-- index accept() --> <p>We will also tidy up the user interface slightly. Click the Update button and set its default property to True. Connect the Close button's <tt>clicked()</tt> signal to the EditBookForm's <tt>accept()</tt> slot.</p>
</ol><h5><a name="3-1-4"></a>Handling Foreign Keys in a <a href="qdatabrowser.html">QDataBrowser</a></h5>
<!-- index Foreign Keys --><!-- index Databases!Foreign Keys --><p>Qt's SQL module provides two approaches to dealing with foreign keys. The most powerful and flexible approach is to subclass widgets and use property maps to relate the widgets to the database. This approach is described in the <a href="http://doc.trolltech.com/sql.html#Custom_Editor_Widgets">Qt SQL Module documentation</a>, particularly the StatusPicker example. A simpler approach that can be taken wholly within <em>Qt Designer</em> is presented here.</p>
<p>We will add a new field to the EditBookForm so that authors can be edited along with the title and price. Once we've handled the visual design we'll write the code to make it all work.</p>
<ol type=1><li><p>First we'll add the new widgets. Click the BookDataBrowser and click the <b>Break Layout</b> toolbar button. Resize the form to make it larger and drag each set of buttons down to make some room below the title and price QLineEdits. Click the <b>Text Label</b> toolbar button and click on the form beneath the Price label. Click the <em>Text Label</em> and change its text to 'Author'. Click the <b>ComboBox</b> toolbar button and click on the form beneath the price QLineEdit. In the Property Window change the <em>ComboBox</em>'s <em>name</em> to ComboBoxAuthor and change its <em>sizePolicy</em> <em>hSizeType</em> to Expanding.</p>
<li><p>Now we'll lay out the dialog. <b>Shift+Click</b> the Author label and the <em>ComboBox</em> then click the <b>Lay Out Horizontally</b> toolbar button. Now click the BookDataBrowser and click the <b>Lay Out in a Grid</b> toolbar button.</p>
</ol><p>We need to write some code so that the <em>ComboBox</em> will be populated with author names and scroll to the current book's author. We also need to ensure that we put the author's id into the book table's authorid field when a book record is inserted or updated. We'll ensure the code is executed at the right time by putting it in slots and connecting signals to our slots.</p>
<ol type=1><li><p>Create two new slots called <tt>beforeUpdateBook(QSqlRecord *buffer)</tt> and <tt>primeUpdateBook(QSqlRecord *buffer)</tt>. (Click <b>Edit|Slots</b>, then in the <em>Edit Functions</em> dialog click New Function and enter the first new slot. Click New Function again and enter the second slot then click <b>OK</b>.)</p>
<li><p>When the user navigates through the dialog, each time they move to a new record, a<!-- index primeUpdate() --> <tt>primeUpdate()</tt> signal is emitted. We connect to this so that we can update the <em>ComboBox</em>'s display. Just before a record is updated or inserted into the database a<!-- index beforeUpdate() --> <tt>beforeUpdate()</tt> or<!-- index beforeInsert() --> <tt>beforeInsert()</tt> signal is emitted. We connect our <tt>beforeUpdateBook()</tt> slot to both these signals so that we can ensure that the book's authorid field is correctly populated.</p>
<p>Click the BookDataBrowser and drag the mouse to the form; release the mouse and the <em>Edit Connections</em> dialog will appear. Connect the<!-- index beforeUpdate() --> <tt>beforeUpdate()</tt> signal to our <tt>beforeUpdateBook()</tt> slot. Connect the<!-- index beforeInsert() --> <tt>beforeInsert()</tt> signal to our <tt>beforeUpdateBook()</tt> slot. Finally connect the<!-- index primeUpdate() --> <tt>primeUpdate()</tt> signal to our <tt>primeUpdateBook()</tt> slot.</p>
<li><p>All that remains is to write the underlying code. All the code snippets are taken from <tt>qt/tools/designer/examples/book/book7/editbook.ui</tt>.</p>
<ul><li><p>We start with the<!-- index init() --> <tt>init()</tt> function; this is called after the dialog is constructed and we will use it to populate the <em>ComboBox</em> with author names.</p>
<pre> void EditBookForm::init()
{
<a href="qsqlquery.html">QSqlQuery</a> query( "SELECT surname FROM author ORDER BY surname;" );
while ( query.<a href="qsqlquery.html#next">next</a>() )
ComboBoxAuthor->insertItem( query.<a href="qsqlquery.html#value">value</a>( 0 ).toString());
}
</pre>
<p>Here we execute a query to get a list of author names and insert each one into the <em>ComboBox</em>.</p>
<li><p>We next write the code which will be executed just before a record is updated (or inserted) in the database.</p>
<pre> void EditBookForm::beforeUpdateBook( <a href="qsqlrecord.html">QSqlRecord</a> * buffer )
{
<a href="qsqlquery.html">QSqlQuery</a> query( "SELECT id FROM author WHERE surname ='" +
ComboBoxAuthor->currentText() + "';" );
if ( query.<a href="qsqlquery.html#next">next</a>() )
buffer-><a href="qsqlrecord.html#setValue">setValue</a>( "authorid", query.<a href="qsqlquery.html#value">value</a>( 0 ) );
}
</pre>
<p>We look up the id of the <em>ComboBox</em>'s current author and place it in the update (or insert) buffer's authorid field.</p>
<li><p>As the user navigates through the records we ensure that the <em>ComboBox</em> reflects the current author.</p>
<pre> void EditBookForm::primeUpdateBook( <a href="qsqlrecord.html">QSqlRecord</a> * buffer )
{
// Who is this book's author?
<a href="qsqlquery.html">QSqlQuery</a> query( "SELECT surname FROM author WHERE id='" +
buffer-><a href="qsqlrecord.html#value">value</a>( "authorid" ).toString() + "';" );
<a href="qstring.html">QString</a> author = "";
if ( query.<a href="qsqlquery.html#next">next</a>() )
author = query.<a href="qsqlquery.html#value">value</a>( 0 ).toString();
// Set the ComboBox to the right author
for ( int i = 0; i < ComboBoxAuthor->count(); i++ ) {
if ( ComboBoxAuthor->text( i ) == author ) {
ComboBoxAuthor->setCurrentItem( i ) ;
break;
}
}
}
</pre>
<p>Firstly we look up the book's author and secondly we iterate through the <em>ComboBox</em>'s items until we find the author and set the <em>ComboBox</em>'s current item to the matching author.</p>
</ul></ol><p>If the author name has changed or been deleted the query will fail and no author id will be inserted into the buffer causing the <tt>INSERT</tt> to fail. An alternative is to record the author id's as we populate the <em>ComboBox</em> and store them in a <a href="qmap.html">QMap</a> which we can then look up as required. This approach requires changes to the<!-- index init() --> <tt>init()</tt>, <tt>beforeUpdateBook()</tt> and <tt>primeInsertBook()</tt> functions and the addition of a new function, <tt>mapAuthor()</tt>. The relevant code from <tt>qt/tools/designer/examples/book/book8/editbook.ui</tt> is shown below.</p>
<ol type=1><li><p>First we need to create a class variable to map author names to author id's. Click in the Members tab of the Object Explorer, then right click the Class Variables item and click <b>New</b>. Type in 'QMap<QString,int> authorMap;'.</p>
<li><p>We now record the author id's in the<!-- index init() --> <tt>init()</tt> function.</p>
<pre> void EditBookForm::init()
{
<a href="qsqlquery.html">QSqlQuery</a> query( "SELECT surname, id FROM author ORDER BY surname;" );
while ( query.<a href="qsqlquery.html#next">next</a>() ) {
ComboBoxAuthor->insertItem( query.<a href="qsqlquery.html#value">value</a>( 0 ).toString() );
int id = query.<a href="qsqlquery.html#value">value</a>( 1 ).toInt();
mapAuthor( query.<a href="qsqlquery.html#value">value</a>( 0 ).toString(), id, TRUE );
}
}
</pre>
<p>After inserting each author's name into the <em>ComboBox</em> we populate a <a href="qmap.html">QMap</a> with the author's name and id.</p>
<li><p>Instead of looking up the author's id in the database we look it up in the <a href="qmap.html">QMap</a>.</p>
<pre> void EditBookForm::beforeUpdateBook( <a href="qsqlrecord.html">QSqlRecord</a> * buffer )
{
int id;
mapAuthor( ComboBoxAuthor->currentText(), id, FALSE );
buffer-><a href="qsqlrecord.html#setValue">setValue</a>( "authorid", id );
}
</pre>
<li> <p>We use a single function for storing author id's and returning them so that we can use a static data structure.</p>
<pre> void EditBookForm::mapAuthor( const <a href="qstring.html">QString</a> & name, int & id, bool populate )
{
if ( populate )
authorMap[ name ] = id;
else
id = authorMap[ name ];
}
</pre>
<p>If the populate flag is TRUE, we store the author's name and id in the <a href="qmap.html">QMap</a>, otherwise we look up the given author name and set id appropriately.</p>
<li><p>Before we perform an update we must ensure that the author combobox shows the right author.</p>
<pre> void EditBookForm::primeUpdateBook( <a href="qsqlrecord.html">QSqlRecord</a> * buffer )
{
int id = buffer-><a href="qsqlrecord.html#value">value</a>( "authorid" ).toInt();
for ( int i = 0; i < ComboBoxAuthor->count(); i++ ) {
<a href="qstring.html">QString</a> author = ComboBoxAuthor->text( i );
if ( authorMap.contains( author ) && authorMap[author] == id ) {
ComboBoxAuthor->setCurrentItem( i ) ;
break;
}
}
}
</pre>
</ol><!-- index Databases!Foreign Keys --><!-- index Foreign Keys --> <p>Another approach which is especially useful if the same foreign key lookups are required in different parts of the application is to subclass a cursor and use this for our lookups. This is described in the <a href="http://doc.trolltech.com/sql.html">Qt SQL Module documentation</a>, particulary the section on subclassing <a href="qsqlcursor.html">QSqlCursor</a>.</p>
<p>The 'book' example demonstrates the basic techniques needed for SQL programming with Qt. Additional information on the Qt SQL classes, especially the <a href="qsqlquery.html">QSqlQuery</a> and <a href="qsqlcursor.html">QSqlCursor</a> classes is provided in the <a href="http://doc.trolltech.com/sql.html">Qt SQL Module documentation</a>.</p>
<!-- eof -->
<p align="right">[<a href="designer-manual-7.html">Prev: Creating Custom Widgets</a>] [<a href="designer-manual.html">Home</a>] [<a href="designer-manual-9.html">Next: Customizing and Integrating Qt Designer</a>]</p>
<p><address><hr><div align=center>
<table width=100% cellspacing=0 border=0><tr>
<td>Copyright © 2007
<a href="troll.html">Trolltech</a><td align=center><a href="trademarks.html">Trademarks</a>
<td align=right><div align=right>Qt 3.3.8</div>
</table></div></address></body>
</html>
|