summaryrefslogtreecommitdiffstats
path: root/doc/html/tutorial2-08.html
blob: a260cb5e20ffb4da34c8d42280f7969fd53b24d9 (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
<!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/doc/tutorial2.doc:1017 -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Taking Data</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&nbsp;Classes</font></a>
 | <a href="mainclasses.html">
<font color="#004faf">Main&nbsp;Classes</font></a>
 | <a href="annotated.html">
<font color="#004faf">Annotated</font></a>
 | <a href="groups.html">
<font color="#004faf">Grouped&nbsp;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><h1 align=center>Taking Data</h1>


<p> 
<p> <center><img src="chart-setdata.png" alt="The set data dialog"></center> 
<p> The set data dialog allows the user to add and edit values, and to
choose the color and pattern used to display values. Users can also
enter label text and choose a label color for each label. 
<p> (Extracts from <tt>setdataform.h</tt>.)
<p> 

<pre>    class SetDataForm: public <a href="qdialog.html">QDialog</a>
    {
        <a href="metaobjects.html#Q_OBJECT">Q_OBJECT</a>
    public:
        SetDataForm( ElementVector *elements, int decimalPlaces,
                     <a href="qwidget.html">QWidget</a> *parent = 0, const char *name = "set data form",
                     bool modal = TRUE, WFlags f = 0 );
        ~SetDataForm() {}

    public slots:
        void setColor();
        void setColor( int row, int col );
        void currentChanged( int row, int col );
        void valueChanged( int row, int col );

    protected slots:
        void accept();

    private:
        <a href="qtable.html">QTable</a> *table;
        <a href="qpushbutton.html">QPushButton</a> *colorPushButton;
        <a href="qpushbutton.html">QPushButton</a> *okPushButton;
        <a href="qpushbutton.html">QPushButton</a> *cancelPushButton;

    protected:
        <a href="qvboxlayout.html">QVBoxLayout</a> *tableButtonBox;
        <a href="qhboxlayout.html">QHBoxLayout</a> *buttonBox;

    private:
        ElementVector *m_elements;
        int m_decimalPlaces;
    };
</pre>
<p> The header file is simple. The constructor takes a pointer to the
element vector so that this "smart" dialog can display and edit the
data directly. We'll explain the slots as we look through the
implementation. 
<p> (Extracts from <tt>setdataform.cpp</tt>.)
<p> 

<pre>    #include "images/pattern01.xpm"
    #include "images/pattern02.xpm"
</pre>
<p> We have created a small <tt>.XPM</tt> image to show each brush pattern that
Qt supports. We'll use these in the pattern combobox.
<p> <h2> The Constructor
</h2>
<a name="1"></a><p> <pre>    SetDataForm::SetDataForm( ElementVector *elements, int decimalPlaces,
                              <a href="qwidget.html">QWidget</a>* parent,  const char* name,
                              bool modal, WFlags f )
        : <a href="qdialog.html">QDialog</a>( parent, name, modal, f )

    {
        m_elements = elements;
        m_decimalPlaces = decimalPlaces;
</pre>
<p> We pass most of the arguments to the <a href="qdialog.html">QDialog</a> superclass. We assign the
elements vector pointer and the number of decimal places to display to
member variables so that they are accessible by all SetDataForm's
member functions.
<p> <pre>        <a href="qwidget.html#setCaption">setCaption</a>( "Chart -- Set Data" );
        <a href="qwidget.html#resize">resize</a>( 540, 440 );
</pre>
<p> We set a caption for the dialog and resize it.
<p> <pre>        tableButtonBox = new <a href="qvboxlayout.html">QVBoxLayout</a>( this, 11, 6, "table button box layout" );
</pre>
<p> The layout of the form is quite simple. The buttons will be grouped
together in a horizontal layout and the table and the button layout
will be grouped together vertically using the tableButtonBox layout.
<p> <pre>        table = new <a href="qtable.html">QTable</a>( this, "data table" );
    <a name="x2621"></a>    table-&gt;<a href="qtable.html#setNumCols">setNumCols</a>( 5 );
    <a name="x2622"></a>    table-&gt;<a href="qtable.html#setNumRows">setNumRows</a>( ChartForm::MAX_ELEMENTS );
    <a name="x2619"></a>    table-&gt;<a href="qtable.html#setColumnReadOnly">setColumnReadOnly</a>( 1, TRUE );
        table-&gt;<a href="qtable.html#setColumnReadOnly">setColumnReadOnly</a>( 2, TRUE );
        table-&gt;<a href="qtable.html#setColumnReadOnly">setColumnReadOnly</a>( 4, TRUE );
    <a name="x2620"></a>    table-&gt;<a href="qtable.html#setColumnWidth">setColumnWidth</a>( 0, 80 );
        table-&gt;<a href="qtable.html#setColumnWidth">setColumnWidth</a>( 1, 60 ); // Columns 1 and 4 must be equal
        table-&gt;<a href="qtable.html#setColumnWidth">setColumnWidth</a>( 2, 60 );
        table-&gt;<a href="qtable.html#setColumnWidth">setColumnWidth</a>( 3, 200 );
        table-&gt;<a href="qtable.html#setColumnWidth">setColumnWidth</a>( 4, 60 );
    <a name="x2616"></a>    <a href="qheader.html">QHeader</a> *th = table-&gt;<a href="qtable.html#horizontalHeader">horizontalHeader</a>();
    <a name="x2605"></a>    th-&gt;<a href="qheader.html#setLabel">setLabel</a>( 0, "Value" );
        th-&gt;<a href="qheader.html#setLabel">setLabel</a>( 1, "Color" );
        th-&gt;<a href="qheader.html#setLabel">setLabel</a>( 2, "Pattern" );
        th-&gt;<a href="qheader.html#setLabel">setLabel</a>( 3, "Label" );
        th-&gt;<a href="qheader.html#setLabel">setLabel</a>( 4, "Color" );
        tableButtonBox-&gt;<a href="qboxlayout.html#addWidget">addWidget</a>( table );
</pre>
<p> We create a new <a href="qtable.html">QTable</a> with five columns, and the same number of rows
as we have elements in the elements vector. We make the color and
pattern columns read only: this is to prevent the user typing in them.
We will make the color changeable by the user clicking on a color or
navigating to a color and clicking the Color button. The pattern will
be in a combobox, changeable simply by the user selecting a different
pattern. Next we set suitable initial widths, insert labels for each
column and finally add the table to the tableButtonBox layout.
<p> <pre>        buttonBox = new <a href="qhboxlayout.html">QHBoxLayout</a>( 0, 0, 6, "button box layout" );
</pre>
<p> We create a horizontal box layout to hold the buttons.
<p> <pre>        colorPushButton = new <a href="qpushbutton.html">QPushButton</a>( this, "color button" );
    <a name="x2598"></a>    colorPushButton-&gt;<a href="qbutton.html#setText">setText</a>( "&amp;Color..." );
        colorPushButton-&gt;<a href="qwidget.html#setEnabled">setEnabled</a>( FALSE );
        buttonBox-&gt;<a href="qboxlayout.html#addWidget">addWidget</a>( colorPushButton );
</pre>
<p> We create a color button and add it to the buttonBox layout. We
disable the button; we will only enable it when the focus is actually
on a color cell.
<p> <pre>        <a href="qspaceritem.html">QSpacerItem</a> *spacer = new <a href="qspaceritem.html">QSpacerItem</a>( 0, 0, QSizePolicy::Expanding,
                                                     QSizePolicy::Minimum );
    <a name="x2593"></a>    buttonBox-&gt;<a href="qboxlayout.html#addItem">addItem</a>( spacer );
</pre>
<p> Since we want to separate the color button from the OK and Cancel
buttons we next create a spacer and add that to the buttonBox layout.
<p> <pre>        okPushButton = new <a href="qpushbutton.html">QPushButton</a>( this, "ok button" );
        okPushButton-&gt;<a href="qbutton.html#setText">setText</a>( "OK" );
    <a name="x2607"></a>    okPushButton-&gt;<a href="qpushbutton.html#setDefault">setDefault</a>( TRUE );
        buttonBox-&gt;<a href="qboxlayout.html#addWidget">addWidget</a>( okPushButton );

        cancelPushButton = new <a href="qpushbutton.html">QPushButton</a>( this, "cancel button" );
        cancelPushButton-&gt;<a href="qbutton.html#setText">setText</a>( "Cancel" );
    <a name="x2597"></a>    cancelPushButton-&gt;<a href="qbutton.html#setAccel">setAccel</a>( Key_Escape );
        buttonBox-&gt;<a href="qboxlayout.html#addWidget">addWidget</a>( cancelPushButton );
</pre>
<p> The OK and Cancel buttons are created and added to the buttonBox. We
make the OK button the dialog's default button, and we make the <tt>Esc</tt>
key an accelerator for the Cancel button.
<p> <pre>    <a name="x2594"></a>    tableButtonBox-&gt;<a href="qboxlayout.html#addLayout">addLayout</a>( buttonBox );
</pre>
<p> We add the buttonBox layout to the tableButtonBox and the layout is
complete.
<p> <pre>    <a name="x2612"></a>    <a href="qobject.html#connect">connect</a>( table, SIGNAL( <a href="qtable.html#clicked">clicked</a>(int,int,int,const <a href="qpoint.html">QPoint</a>&amp;) ),
                 this, SLOT( setColor(int,int) ) );
    <a name="x2613"></a>    <a href="qobject.html#connect">connect</a>( table, SIGNAL( <a href="qtable.html#currentChanged">currentChanged</a>(int,int) ),
                 this, SLOT( currentChanged(int,int) ) );
    <a name="x2626"></a>    <a href="qobject.html#connect">connect</a>( table, SIGNAL( <a href="qtable.html#valueChanged">valueChanged</a>(int,int) ),
                 this, SLOT( valueChanged(int,int) ) );
    <a name="x2596"></a>    <a href="qobject.html#connect">connect</a>( colorPushButton, SIGNAL( <a href="qbutton.html#clicked">clicked</a>() ), this, SLOT( setColor() ) );
        <a href="qobject.html#connect">connect</a>( okPushButton, SIGNAL( <a href="qbutton.html#clicked">clicked</a>() ), this, SLOT( <a href="qdialog.html#accept">accept</a>() ) );
        <a href="qobject.html#connect">connect</a>( cancelPushButton, SIGNAL( <a href="qbutton.html#clicked">clicked</a>() ), this, SLOT( <a href="qdialog.html#reject">reject</a>() ) );
</pre>
<p> We now "wire up" the form. 
<ul>
<li> If the user clicks a cell we call the setColor() slot; this will
check that the cell is one that holds a color, and if it is, will
invoke the color dialog. 
<li> We connect the <a href="qtable.html">QTable</a>'s currentChanged() signal to our own
currentChanged() slot; this will be used to enable/disable the color
button for example, depending on which column the user is in.
<li> We connect the table's valueChanged() signal to our own
valueChanged() slot; we'll use this to display the value with the
correct number of decimal places.
<li> If the user clicks the Color button we call a setColor() slot.
<li> The OK button is connected to the accept() slot; we will update the
elements vector in this slot.
<li> The Cancel button is connected to the <a href="qdialog.html">QDialog</a> reject() slot, and
requires no further code or action on our part.
</ul>
<p> <pre>        QPixmap patterns[MAX_PATTERNS];
        patterns[0]  = QPixmap( pattern01 );
        patterns[1]  = QPixmap( pattern02 );
</pre>
<p> We create a pixmap for every brush pattern and store them in the <tt>patterns</tt> array.
<p> <pre>    <a name="x2610"></a>    <a href="qrect.html">QRect</a> rect = table-&gt;<a href="qtable.html#cellRect">cellRect</a>( 0, 1 );
        <a href="qpixmap.html">QPixmap</a> pix( rect.<a href="qrect.html#width">width</a>(), rect.<a href="qrect.html#height">height</a>() );
</pre>
<p> We obtain the rectangle that will be occupied by each color cell and
create a blank pixmap of that size.
<p> <pre>        for ( int i = 0; i &lt; ChartForm::MAX_ELEMENTS; ++i ) {
            Element element = (*m_elements)[i];

            if ( element.isValid() )
                table-&gt;<a href="qtable.html#setText">setText</a>(
                    i, 0,
                    QString( "%1" ).arg( element.value(), 0, 'f',
                                         m_decimalPlaces ) );

            <a href="qcolor.html">QColor</a> color = element.valueColor();
            pix.<a href="qpixmap.html#fill">fill</a>( color );
            table-&gt;<a href="qtable.html#setPixmap">setPixmap</a>( i, 1, pix );
            table-&gt;<a href="qtable.html#setText">setText</a>( i, 1, color.<a href="qcolor.html#name">name</a>() );

            <a href="qcombobox.html">QComboBox</a> *combobox = new <a href="qcombobox.html">QComboBox</a>;
            for ( int j = 0; j &lt; MAX_PATTERNS; ++j )
                combobox-&gt;<a href="qcombobox.html#insertItem">insertItem</a>( patterns[j] );
    <a name="x2603"></a>        combobox-&gt;<a href="qcombobox.html#setCurrentItem">setCurrentItem</a>( element.valuePattern() - 1 );
    <a name="x2618"></a>        table-&gt;<a href="qtable.html#setCellWidget">setCellWidget</a>( i, 2, combobox );

            table-&gt;<a href="qtable.html#setText">setText</a>( i, 3, element.label() );

            color = element.labelColor();
    <a name="x2606"></a>        pix.<a href="qpixmap.html#fill">fill</a>( color );
    <a name="x2623"></a>        table-&gt;<a href="qtable.html#setPixmap">setPixmap</a>( i, 4, pix );
    <a name="x2624"></a><a name="x2600"></a>        table-&gt;<a href="qtable.html#setText">setText</a>( i, 4, color.<a href="qcolor.html#name">name</a>() );
</pre>
<p> For each element in the element vector we must populate the table.
<p> If the element is valid we write its value in the first column (column
0, Value), formatting it with the specified number of decimal places.
<p> We read the element's value color and fill the blank pixmap with that
color; we then set the color cell to display this pixmap. We need to
be able to read back the color later (e.g. if the user changes it).
One way of doing this would be to examine a pixel in the pixmap;
another way would be to subclass <a href="qtableitem.html">QTableItem</a> (in a similar way to our
CanvasText subclass) and store the color there. But we've taken a
simpler route: we set the cell's text to the name of the color.
<p> Next we populate the pattern combobox with the patterns. We will use
the position of the chosen pattern in the combobox to determine which
pattern the user has selected. <a href="qtable.html">QTable</a> can make use of <a href="qcombotableitem.html">QComboTableItem</a>
items; but these only support text, so we use setCellWidget() to
insert <a href="qcombobox.html">QComboBox</a>'s into the table instead.
<p> Next we insert the element's label. Finally we set the label color in
the same way as we set the value color.
<p> <h2> The Slots
</h2>
<a name="2"></a><p> <pre>    void SetDataForm::currentChanged( int, int col )
    {
        colorPushButton-&gt;<a href="qwidget.html#setEnabled">setEnabled</a>( col == 1 || col == 4 );
    }
</pre>
<p> As the user navigates through the table currentChanged() signals are
emitted. If the user enters column 1 or 4 (value color or label color)
we enable the colorPushButton; otherwise we disable it.
<p> <pre>    void SetDataForm::valueChanged( int row, int col )
    {
        if ( col == 0 ) {
            bool ok;
    <a name="x2625"></a>        double d = table-&gt;<a href="qtable.html#text">text</a>( row, col ).toDouble( &amp;ok );
            if ( ok &amp;&amp; d &gt; EPSILON )
                table-&gt;<a href="qtable.html#setText">setText</a>(
                    row, col, QString( "%1" ).arg(
                                d, 0, 'f', m_decimalPlaces ) );
            else if ( !table-&gt;<a href="qtable.html#text">text</a>( row, col ).isEmpty() )
                table-&gt;<a href="qtable.html#setText">setText</a>( row, col, table-&gt;<a href="qtable.html#text">text</a>( row, col ) + "?" );
        }
    }
</pre>
<p> If the user changes the value we must format it using the correct
number of decimal places, or indicate that it is invalid.
<p> <pre>    void SetDataForm::setColor()
    {
    <a name="x2615"></a><a name="x2614"></a>    setColor( table-&gt;<a href="qtable.html#currentRow">currentRow</a>(), table-&gt;<a href="qtable.html#currentColumn">currentColumn</a>() );
        table-&gt;<a href="qwidget.html#setFocus">setFocus</a>();
    }
</pre>
<p> If the user presses the Color button we call the other setColor()
function and put the focus back into the table.
<p> <pre>    void SetDataForm::setColor( int row, int col )
    {
        if ( !( col == 1 || col == 4 ) )
            return;

    <a name="x2601"></a>    <a href="qcolor.html">QColor</a> color = QColorDialog::<a href="qcolordialog.html#getColor">getColor</a>(
                            QColor( table-&gt;<a href="qtable.html#text">text</a>( row, col ) ),
                            this, "color dialog" );
    <a name="x2599"></a>    if ( color.<a href="qcolor.html#isValid">isValid</a>() ) {
    <a name="x2617"></a>        <a href="qpixmap.html">QPixmap</a> pix = table-&gt;<a href="qtable.html#pixmap">pixmap</a>( row, col );
            pix.<a href="qpixmap.html#fill">fill</a>( color );
            table-&gt;<a href="qtable.html#setPixmap">setPixmap</a>( row, col, pix );
            table-&gt;<a href="qtable.html#setText">setText</a>( row, col, color.<a href="qcolor.html#name">name</a>() );
        }
    }
</pre>
<p> If this function is called with the focus on a color cell we call
the static <a href="qcolordialog.html#getColor">QColorDialog::getColor</a>() dialog to get the user's choice of
color. If they chose a color we fill the color cell's pixmap with that
color and set the cell's text to the new color's name.
<p> <pre>    <a name="x2604"></a>void SetDataForm::<a href="qdialog.html#accept">accept</a>()
    {
        bool ok;
        for ( int i = 0; i &lt; ChartForm::MAX_ELEMENTS; ++i ) {
            Element &amp;element = (*m_elements)[i];
            double d = table-&gt;<a href="qtable.html#text">text</a>( i, 0 ).toDouble( &amp;ok );
            if ( ok )
                element.setValue( d );
            else
                element.setValue( Element::INVALID );
            element.setValueColor( QColor( table-&gt;<a href="qtable.html#text">text</a>( i, 1 ) ) );
            element.setValuePattern(
    <a name="x2611"></a>                ((QComboBox*)table-&gt;<a href="qtable.html#cellWidget">cellWidget</a>( i, 2 ))-&gt;currentItem() + 1 );
            element.setLabel( table-&gt;<a href="qtable.html#text">text</a>( i, 3 ) );
            element.setLabelColor( QColor( table-&gt;<a href="qtable.html#text">text</a>( i, 4 ) ) );
        }

        QDialog::<a href="qdialog.html#accept">accept</a>();
    }
</pre>
<p> If the user clicks OK we must update the elements vector. We iterate
over the vector and set each element's value to the value the user has
entered or <tt>INVALID</tt> if the value is invalid. We set the value color
and the label color by constructing <a href="qcolor.html">QColor</a> temporaries that take a
color name as argument. The pattern is set to the pattern combobox's
current item with an offset of 1 (since our pattern numbers begin at
1, but the combobox's items are indexed from 0).
<p> Finally we call <a href="qdialog.html#accept">QDialog::accept</a>().
<p> <p align="right">
<a href="tutorial2-07.html">&laquo; File Handling</a> |
<a href="tutorial2.html">Contents</a> |
<a href="tutorial2-09.html">Setting Options &raquo;</a>
</p>
<p> 
<!-- eof -->
<p><address><hr><div align=center>
<table width=100% cellspacing=0 border=0><tr>
<td>Copyright &copy; 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>