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
|
<!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:110 -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Data Elements</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><h1 align=center>Data Elements</h1>
<p>
<p> We will use a C++ class called <tt>Element</tt> to provide storage and
access for data elements.
<p> (Extracts from <tt>element.h</tt>.)
<p>
<pre> private:
</pre><pre> double m_value;
<a href="qcolor.html">QColor</a> m_valueColor;
int m_valuePattern;
<a href="qstring.html">QString</a> m_label;
<a href="qcolor.html">QColor</a> m_labelColor;
double m_propoints[2 * MAX_PROPOINTS];
</pre>
<p> Each element has a value. Each value is displayed graphically with a
particular color and fill pattern. Values may have a label associated
with them; the label is drawn using the label color and for each type
of chart has a (relative) position stored in the <tt>m_propoints</tt> array.
<p>
<pre> #include <<a href="qcolor-h.html">qcolor.h</a>>
#include <<a href="qnamespace-h.html">qnamespace.h</a>>
#include <<a href="qstring-h.html">qstring.h</a>>
#include <<a href="qvaluevector-h.html">qvaluevector.h</a>>
</pre>
<p> Although the <tt>Element</tt> class is a purely internal data class, it
<tt>#include</tt>s four Qt classes. Qt is often perceived as a purely GUI
toolkit, but it provides many non-GUI classes to support most aspects
of application programming. We use <a href="qcolor-h.html">qcolor.h</a> so that we can hold the
paint color and text color in the <tt>Element</tt> class. The use of <a href="qnamespace-h.html">qnamespace.h</a> is slightly obscure. Most Qt classes are derived from the
<a href="qt.html">Qt</a> superclass which contains various
enumerations. The <tt>Element</tt> class does not derive from <a href="qt.html">Qt</a>, so we need to include <a href="qnamespace-h.html">qnamespace.h</a> to have access to
the Qt enum names. An alternative approach would have been to have
made <tt>Element</tt> a <a href="qt.html">Qt</a> subclass. We include <a href="qstring-h.html">qstring.h</a> to make use of Qt's Unicode strings. As a convenience we
will <tt>typedef</tt> a vector container for <tt>Element</tt>s, which is why we
pull in the <a href="qvaluevector-h.html">qvaluevector.h</a> header.
<p> <pre> typedef QValueVector<Element> ElementVector;
</pre>
<p> Qt provides a number of containers, some value based like
<a href="qvaluevector.html">QValueVector</a>, and others pointer based. (See <a href="collection.html">Collection Classes</a>.) Here we've just typedefed one container
type; we will keep each data set of elements in one <tt>ElementVector</tt>.
<p> <pre> const double EPSILON = 0.0000001; // Must be > INVALID.
</pre>
<p> Elements may only have positive values. Because we hold values as
doubles we cannot readily compare them with zero. Instead we specify a
value, <tt>EPSILON</tt>, which is close to zero, and consider any value
greater than <tt>EPSILON</tt> to be positive and valid.
<p> <pre> class Element
{
public:
enum { INVALID = -1 };
enum { NO_PROPORTION = -1 };
enum { MAX_PROPOINTS = 3 }; // One proportional point per chart type
</pre>
<p> We define three public enums for <tt>Element</tt>s. <tt>INVALID</tt> is used by
the isValid() function. It is useful because we are going to use a
fixed size vector of <tt>Element</tt>s, and can mark unused <tt>Element</tt>s by
giving them <tt>INVALID</tt> values. The <tt>NO_PROPORTION</tt> enum is used to
signify that the user has not positioned the Element's label; any
positive proportion value is taken to be the text element's position
proportional to the canvas's size.
<p> If we stored each label's actual x and y position, then every time the
user resized the main window (and therefore the canvas), the text
would retain its original (now incorrect) position. So instead of
storing absolute (x, y) positions we store <em>proportional</em> positions,
i.e. x/width and y/height. We can then multiply these positions by
the current width and height respectively when we come to draw the
text and the text will be positioned correctly regardless of any
resizing. For example, if a label has an x position of 300 and the
canvas is 400 pixels wide, the proportional x value is 300/400 = 0.75.
<p> The <tt>MAX_PROPOINTS</tt> enum is problematic. We need to store the x and y
proportions for the text label for every chart type. And we have
chosen to store these proportions in a fixed-size array. Because of
this we must specify the maximum number of proportion pairs needed.
This value must be changed if we change the number of chart types,
which means that the <tt>Element</tt> class is strongly coupled to the
number of chart types provided by the <tt>ChartForm</tt> class. In a
larger application we might have used a vector to store these points
and dynamically resized it depending on how many chart types are
available.
<p> <pre> Element( double value = INVALID, QColor valueColor = Qt::gray,
int valuePattern = Qt::SolidPattern,
const <a href="qstring.html">QString</a>& label = <a href="qstring.html#QString-null">QString::null</a>,
<a href="qcolor.html">QColor</a> labelColor = Qt::black ) {
init( value, valueColor, valuePattern, label, labelColor );
for ( int i = 0; i < MAX_PROPOINTS * 2; ++i )
m_propoints[i] = NO_PROPORTION;
}
</pre>
<p> The constructor provides default values for all members of the <tt>Element</tt> class. New elements always have label text with no position.
We use an init() function because we also provide a set() function
which works like the constructor apart from leaving the proportional
positions alone.
<p> <pre> bool isValid() const { return m_value > EPSILON; }
</pre>
<p> Since we are storing <tt>Element</tt>s in a fixed size vector we need to be
able to check whether a particular element is valid (i.e. should be
used in calculations and displayed) or not. This is easily achieved
with the isValid() function.
<p> (Extracts from <tt>element.cpp</tt>.)
<p>
<pre> double Element::proX( int index ) const
{
<a href="qapplication.html#Q_ASSERT">Q_ASSERT</a>(index >= 0 && index < MAX_PROPOINTS);
return m_propoints[2 * index];
}
</pre>
<p> Getters and setters are provided for all the members of <tt>Element</tt>.
The proX() and proY() getters and the setProX() and setProY() setters
take an index which identifies the type of chart the proportional
position applies to. This means that the user can have labels
positioned separately for the same data set for a vertical bar chart,
a horizontal bar chart and for a pie chart. Note also that we use the
<tt>Q_ASSERT</tt> macro to provide pre-condition tests on the chart type
index; (see <a href="debug.html">Debugging</a>).
<p> <h2> Reading and Writing Data Elements
</h2>
<a name="1"></a><p> (Extracts from <tt>element.h</tt>.)
<p>
<pre> QTextStream &operator<<( <a href="qtextstream.html">QTextStream</a>&, const Element& );
QTextStream &operator>>( <a href="qtextstream.html">QTextStream</a>&, Element& );
</pre>
<p> To make our <tt>Element</tt> class more self-contained we provide overloads
for the << and >> operators so that <tt>Element</tt>s may be written to
and read from text streams. We could just as easily have used binary
streams, but using text makes it possible for users to manipulate
their data using a text editor and makes it easier to generate and
filter the data using a scripting language.
<p> (Extracts from <tt>element.cpp</tt>.)
<p>
<pre> #include "element.h"
#include <<a href="qstringlist-h.html">qstringlist.h</a>>
#include <<a href="qtextstream-h.html">qtextstream.h</a>>
</pre>
<p> Our implementation of the operators requires the inclusion of <a href="qtextstream-h.html">qtextstream.h</a> and <a href="qstringlist-h.html">qstringlist.h</a>.
<p> <pre> const char FIELD_SEP = ':';
const char PROPOINT_SEP = ';';
const char XY_SEP = ',';
</pre>
<p> The format we are using to store the data is colon separated fields
and newline separated records. The proportional points are semi-colon
separated, with their x, y pairs being comma separated. The field
order is value, value color, value pattern, label color, label points,
label text. For example:
<pre>
20:#ff0000:14:#000000:0.767033,0.412946;0,0.75;0,0:Red :with colons:!
70:#00ffff:2:#ffff00:0.450549,0.198661;0.198516,0.125954;0,0.198473:Cyan
35:#0000ff:8:#555500:0.10989,0.299107;0.397032,0.562977;0,0.396947:Blue
55:#ffff00:1:#000080:0.0989011,0.625;0.595547,0.312977;0,0.59542:Yellow
80:#ff00ff:1:#000000:0.518681,0.694196;0.794063,0;0,0.793893:Magenta or Violet
</pre>
<p> There's no problem having whitespace and field separators in label
text due to the way we read <tt>Element</tt> data.
<p> <pre> QTextStream &operator<<( <a href="qtextstream.html">QTextStream</a> &s, const Element &element )
{
s << element.value() << FIELD_SEP
<< element.valueColor().name() << FIELD_SEP
<< element.valuePattern() << FIELD_SEP
<< element.labelColor().name() << FIELD_SEP;
for ( int i = 0; i < Element::MAX_PROPOINTS; ++i ) {
s << element.proX( i ) << XY_SEP << element.proY( i );
s << ( i == Element::MAX_PROPOINTS - 1 ? FIELD_SEP : PROPOINT_SEP );
}
s << element.label() << '\n';
return s;
}
</pre>
<p> Writing elements is straight-forward. Each member is written followed
by a field separator. The points are written as comma separated (<tt>XY_SEP</tt>) x, y pairs, each pair separated by the <tt>PROPOINT_SEP</tt>
separator. The final field is the label followed by a newline.
<p> <pre> QTextStream &operator>>( <a href="qtextstream.html">QTextStream</a> &s, Element &element )
{
<a name="x2553"></a> <a href="qstring.html">QString</a> data = s.<a href="qtextstream.html#readLine">readLine</a>();
element.setValue( Element::INVALID );
int errors = 0;
bool ok;
<a name="x2552"></a> <a href="qstringlist.html">QStringList</a> fields = QStringList::<a href="qstringlist.html#split">split</a>( FIELD_SEP, data );
<a name="x2555"></a> if ( fields.<a href="qvaluelist.html#count">count</a>() >= 4 ) {
double value = fields[0].toDouble( &ok );
if ( !ok )
errors++;
<a href="qcolor.html">QColor</a> valueColor = QColor( fields[1] );
<a name="x2550"></a> if ( !valueColor.<a href="qcolor.html#isValid">isValid</a>() )
errors++;
int valuePattern = fields[2].toInt( &ok );
if ( !ok )
errors++;
<a href="qcolor.html">QColor</a> labelColor = QColor( fields[3] );
if ( !labelColor.<a href="qcolor.html#isValid">isValid</a>() )
errors++;
<a href="qstringlist.html">QStringList</a> propoints = QStringList::<a href="qstringlist.html#split">split</a>( PROPOINT_SEP, fields[4] );
<a name="x2551"></a> <a href="qstring.html">QString</a> label = data.<a href="qstring.html#section">section</a>( FIELD_SEP, 5 );
if ( !errors ) {
element.set( value, valueColor, valuePattern, label, labelColor );
int i = 0;
<a name="x2554"></a> for ( QStringList::iterator point = propoints.<a href="qvaluelist.html#begin">begin</a>();
<a name="x2556"></a> i < Element::MAX_PROPOINTS && point != propoints.<a href="qvaluelist.html#end">end</a>();
++i, ++point ) {
errors = 0;
<a href="qstringlist.html">QStringList</a> xy = QStringList::<a href="qstringlist.html#split">split</a>( XY_SEP, *point );
double x = xy[0].toDouble( &ok );
if ( !ok || x <= 0.0 || x >= 1.0 )
errors++;
double y = xy[1].toDouble( &ok );
if ( !ok || y <= 0.0 || y >= 1.0 )
errors++;
if ( errors )
x = y = Element::NO_PROPORTION;
element.setProX( i, x );
element.setProY( i, y );
}
}
}
return s;
}
</pre>
<p> To read an element we read one record (i.e. one line). We break the
data into fields using <a href="qstringlist.html#split">QStringList::split</a>(). Because it is possible
that a label will contain <tt>FIELD_SEP</tt> characters we use
<a href="qstring.html#section">QString::section</a>() to extract all the text from the last field to the
end of the line. If there are enough fields and the value, colors and
pattern data is valid we use <tt>Element::set()</tt> to write this data into
the element; otherwise we leave the element <tt>INVALID</tt>. We then
iterate through the points. If the x and y proportions are valid and
in range we set them for the element. If one or both proportions is
invalid they will hold the value zero; this is not suitable so we
change invalid (and out-of-range) proportional point values to <tt>NO_PROPORTION</tt>.
<p> Our <tt>Element</tt> class is now sufficient to store, manipulate, read and
write element data. We have also created an element vector typedef for
storing a collection of elements.
<p> We are now ready to create <tt>main.cpp</tt> and the user interface through
which our users will create, edit and visualise their data sets.
<p> <center><table cellpadding="4" cellspacing="2" border="0">
<tr bgcolor="#f0f0f0">
<td valign="top">For more information on Qt's data streaming facilities see <a href="datastreamformat.html">QDataStream Operators' Formats</a>, and see
the source code for any of the Qt classes mentioned that are similar
to what you want to store.
</table></center>
<p> <p align="right">
<a href="tutorial2-02.html">« The 'Big Picture'</a> |
<a href="tutorial2.html">Contents</a> |
<a href="tutorial2-04.html">Mainly Easy »</a>
</p>
<p>
<!-- eof -->
<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>
|