You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
qt3/doc/html/tutorial2-03.html

299 lines
15 KiB

<!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&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>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 &lt;<a href="qcolor-h.html">qcolor.h</a>&gt;
#include &lt;<a href="qnamespace-h.html">qnamespace.h</a>&gt;
#include &lt;<a href="qstring-h.html">qstring.h</a>&gt;
#include &lt;<a href="qvaluevector-h.html">qvaluevector.h</a>&gt;
</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&lt;Element&gt; 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 &gt; 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>&amp; 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 &lt; 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 &gt; 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 &gt;= 0 &amp;&amp; index &lt; 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 &amp;operator&lt;&lt;( <a href="qtextstream.html">QTextStream</a>&amp;, const Element&amp; );
QTextStream &amp;operator&gt;&gt;( <a href="qtextstream.html">QTextStream</a>&amp;, Element&amp; );
</pre>
<p> To make our <tt>Element</tt> class more self-contained we provide overloads
for the &lt;&lt; and &gt;&gt; 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 &lt;<a href="qstringlist-h.html">qstringlist.h</a>&gt;
#include &lt;<a href="qtextstream-h.html">qtextstream.h</a>&gt;
</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 &amp;operator&lt;&lt;( <a href="qtextstream.html">QTextStream</a> &amp;s, const Element &amp;element )
{
s &lt;&lt; element.value() &lt;&lt; FIELD_SEP
&lt;&lt; element.valueColor().name() &lt;&lt; FIELD_SEP
&lt;&lt; element.valuePattern() &lt;&lt; FIELD_SEP
&lt;&lt; element.labelColor().name() &lt;&lt; FIELD_SEP;
for ( int i = 0; i &lt; Element::MAX_PROPOINTS; ++i ) {
s &lt;&lt; element.proX( i ) &lt;&lt; XY_SEP &lt;&lt; element.proY( i );
s &lt;&lt; ( i == Element::MAX_PROPOINTS - 1 ? FIELD_SEP : PROPOINT_SEP );
}
s &lt;&lt; element.label() &lt;&lt; '\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 &amp;operator&gt;&gt;( <a href="qtextstream.html">QTextStream</a> &amp;s, Element &amp;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>() &gt;= 4 ) {
double value = fields[0].toDouble( &amp;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( &amp;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 &lt; Element::MAX_PROPOINTS &amp;&amp; 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( &amp;ok );
if ( !ok || x &lt;= 0.0 || x &gt;= 1.0 )
errors++;
double y = xy[1].toDouble( &amp;ok );
if ( !ok || y &lt;= 0.0 || y &gt;= 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">&laquo; The 'Big Picture'</a> |
<a href="tutorial2.html">Contents</a> |
<a href="tutorial2-04.html">Mainly Easy &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>