summaryrefslogtreecommitdiffstats
path: root/doc/tutorial2.doc
diff options
context:
space:
mode:
Diffstat (limited to 'doc/tutorial2.doc')
-rw-r--r--doc/tutorial2.doc1435
1 files changed, 1435 insertions, 0 deletions
diff --git a/doc/tutorial2.doc b/doc/tutorial2.doc
new file mode 100644
index 0000000..ad20fef
--- /dev/null
+++ b/doc/tutorial2.doc
@@ -0,0 +1,1435 @@
+/*! \file chart/chart.pro */
+/*! \file chart/element.h */
+/*! \file chart/element.cpp */
+/*! \file chart/main.cpp */
+/*! \file chart/canvastext.h */
+/*! \file chart/canvasview.h */
+/*! \file chart/canvasview.cpp */
+/*! \file chart/chartform.h */
+/*! \file chart/chartform.cpp */
+/*! \file chart/chartform_canvas.cpp */
+/*! \file chart/chartform_files.cpp */
+/*! \file chart/optionsform.h */
+/*! \file chart/optionsform.cpp */
+/*! \file chart/setdataform.h */
+/*! \file chart/setdataform.cpp */
+
+/*!
+
+\page tutorial2.html
+
+\title Tutorial #2
+
+This tutorial presents a more "real world" example of Qt programming
+than the first tutorial. It introduces many aspects of Qt programming,
+including the creation of menus (including a recent files list),
+toolbars and dialogs, loading and saving user settings, etc.
+
+If you're completely new to Qt, please read \link how-to-learn-qt.html
+How to Learn Qt\endlink if you haven't already done so.
+
+\list
+\i \link tutorial2-01.html Introduction\endlink
+\i \link tutorial2-02.html The 'Big Picture'\endlink
+\i \link tutorial2-03.html Data Elements\endlink
+\i \link tutorial2-04.html Mainly Easy\endlink
+\i \link tutorial2-05.html Presenting the GUI\endlink
+\i \link tutorial2-06.html Canvas Control\endlink
+\i \link tutorial2-07.html File Handling\endlink
+\i \link tutorial2-08.html Taking Data\endlink
+\i \link tutorial2-09.html Setting Options\endlink
+\i \link tutorial2-10.html The Project File\endlink
+\i \link tutorial2-11.html Wrapping Up\endlink
+\endlist
+
+<p align="right">
+<a href="tutorial2-01.html">Introduction &raquo;</a>
+</p>
+*/
+
+/*!
+
+\page tutorial2-01.html
+
+\title Introduction
+
+In this tutorial we will develop a single application called \c chart,
+which is used to display simple pie and bar charts based on data that
+the user enters.
+
+The tutorial gives an overview of the development of the application
+and includes code snippets and accompanying explanations. The complete
+source for the application is in \c examples/chart.
+
+\img chart-main.png The chart application
+
+<p align="right">
+<a href="tutorial2.html">&laquo; Contents</a> |
+<a href="tutorial2-02.html">The 'Big Picture' &raquo;</a>
+</p>
+
+*/
+
+/*!
+
+\page tutorial2-02.html
+
+\title The 'Big Picture'
+
+\img chart-forms.png The chart application's dialogs
+
+The \c chart program allows users to create, save, load and visualise
+simple data sets. Each data element that the user enters can be given
+a color and pattern for the pie segment or bar, some label text and
+the text's position and color. The \c Element class is used to
+represent data elements.
+
+The program consists of a simple \c main.cpp that loads the chart
+form. The chart form has a menubar and toolbar which provide access to
+the program's functionality. The program provides two dialogs, one to
+set options, and the other to create and edit a data set. Both dialogs
+are launched from chart form menu options or toolbar buttons.
+
+The chart form's main widget is a QCanvasView which displays the
+QCanvas on which we draw the pie chart or bar graph. We subclass
+QCanvasView to obtain some specialised behaviour. Similarly we
+subclass the QCanvasText class (used to place text items on a canvas)
+since we require slightly more than the standard class provides.
+
+The project file, \c chart.pro, is used to create the Makefile that is
+used to build the application.
+
+<p align="right">
+<a href="tutorial2-01.html">&laquo; Introduction</a> |
+<a href="tutorial2.html">Contents</a> |
+<a href="tutorial2-03.html">Data Elements &raquo;</a>
+</p>
+
+*/
+
+/*!
+
+\page tutorial2-03.html
+
+\title Data Elements
+
+We will use a C++ class called \c Element to provide storage and
+access for data elements.
+
+(Extracts from \c element.h.)
+
+\quotefile chart/element.h
+\skipto private
+\printline
+\skipto m_value
+\printto };
+
+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 \c m_propoints array.
+
+\quotefile chart/element.h
+\skipto #include
+\printto class
+
+Although the \c Element class is a purely internal data class, it
+\c{#include}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 \c qcolor.h so that we can hold the
+paint color and text color in the \c Element class. The use of \c
+qnamespace.h is slightly obscure. Most Qt classes are derived from the
+\link qt.html Qt\endlink superclass which contains various
+enumerations. The \c Element class does not derive from \link qt.html
+Qt\endlink, so we need to include \c qnamespace.h to have access to
+the Qt enum names. An alternative approach would have been to have
+made \c Element a \link qt.html Qt\endlink subclass. We include \c
+qstring.h to make use of Qt's Unicode strings. As a convenience we
+will \c typedef a vector container for \c{Element}s, which is why we
+pull in the \c qvaluevector.h header.
+
+\skipto QValueVector
+\printline
+
+Qt provides a number of containers, some value based like
+QValueVector, and others pointer based. (See \link collection.html
+Collection Classes\endlink.) Here we've just typedefed one container
+type; we will keep each data set of elements in one \c ElementVector.
+
+\skipto const double EPSILON
+\printline
+
+Elements may only have positive values. Because we hold values as
+doubles we cannot readily compare them with zero. Instead we specify a
+value, \c EPSILON, which is close to zero, and consider any value
+greater than \c EPSILON to be positive and valid.
+
+\skipto class
+\printto Element(
+
+We define three public enums for \c{Element}s. \c INVALID is used by
+the isValid() function. It is useful because we are going to use a
+fixed size vector of \c{Element}s, and can mark unused \c{Element}s by
+giving them \c INVALID values. The \c NO_PROPORTION 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.
+
+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 \e proportional 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.
+
+The \c MAX_PROPOINTS 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 \c Element class is strongly coupled to the
+number of chart types provided by the \c ChartForm 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.
+
+\printto Element()
+
+The constructor provides default values for all members of the \c
+Element 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.
+
+\skipto isValid()
+\printline
+
+Since we are storing \c{Element}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.
+
+(Extracts from \c element.cpp.)
+
+\quotefile chart/element.cpp
+\skipto Element::proX
+\printuntil }
+
+Getters and setters are provided for all the members of \c Element.
+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
+\c Q_ASSERT macro to provide pre-condition tests on the chart type
+index; (see \link debug.html Debugging\endlink).
+
+\section1 Reading and Writing Data Elements
+
+(Extracts from \c element.h.)
+
+\quotefile chart/element.h
+\skipto QTextStream
+\printline
+\printline
+
+To make our \c Element class more self-contained we provide overloads
+for the \<\< and \>\> operators so that \c{Element}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.
+
+(Extracts from \c element.cpp.)
+
+\quotefile chart/element.cpp
+\skipto include
+\printto const
+
+Our implementation of the operators requires the inclusion of \c
+qtextstream.h and \c qstringlist.h.
+
+\printto Element
+
+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:
+\code
+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
+\endcode
+
+There's no problem having whitespace and field separators in label
+text due to the way we read \c Element data.
+
+\skipto &operator<<
+\printuntil return
+\printline
+
+Writing elements is straight-forward. Each member is written followed
+by a field separator. The points are written as comma separated (\c
+XY_SEP) x, y pairs, each pair separated by the \c PROPOINT_SEP
+separator. The final field is the label followed by a newline.
+
+\skipto &operator>>
+\printuntil return
+\printline
+
+To read an element we read one record (i.e. one line). We break the
+data into fields using QStringList::split(). Because it is possible
+that a label will contain \c FIELD_SEP characters we use
+QString::section() 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 \c Element::set() to write this data into
+the element; otherwise we leave the element \c INVALID. 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 \c
+NO_PROPORTION.
+
+Our \c Element 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.
+
+We are now ready to create \c main.cpp and the user interface through
+which our users will create, edit and visualise their data sets.
+
+\table
+\row
+\i For more information on Qt's data streaming facilities see \link
+datastreamformat.html QDataStream Operators' Formats\endlink, and see
+the source code for any of the Qt classes mentioned that are similar
+to what you want to store.
+\endtable
+
+<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>
+
+*/
+
+/*!
+
+\page tutorial2-04.html
+
+\title Mainly Easy
+
+(\c main.cpp.)
+
+\quotefile chart/main.cpp
+\printuntil return
+\printline
+
+We have kept the main() function simple and small. We create a
+QApplication object and pass it the command line arguments. We are
+allowing users to invoke the program with \c{chart mychart.cht}, so if
+they've added a filename we pass that through to the chart form
+constructor. Most of the action takes place within the chart form
+which we'll review next.
+
+<p align="right">
+<a href="tutorial2-03.html">&laquo; Data Elements</a> |
+<a href="tutorial2.html">Contents</a> |
+<a href="tutorial2-05.html">Presenting the GUI &raquo;</a>
+</p>
+
+*/
+
+/*!
+
+\page tutorial2-05.html
+
+\title Presenting the GUI
+
+\img chart-main2.png The chart application
+
+The \c chart application provides access to options via menus and
+toolbar buttons arranged around a central widget, a CanvasView, in a
+conventional document-centric style.
+
+(Extracts from \c chartform.h.)
+
+\quotefile chart/chartform.h
+\skipto public QMainWindow
+\printuntil optionsVerticalBarChartAction
+\printuntil };
+
+We create a \c ChartForm subclass of QMainWindow. Our subclass uses
+the \c Q_OBJECT macro to support Qt's \link signalsandslots.html
+signals and slots\endlink mechanism.
+
+The public interface is very small; the type of chart being displayed
+can be retrieved, the chart can be marked 'changed' (so that the user
+will be prompted to save on exit), and the chart can be asked to draw
+itself (drawElements()). We've also made the options menu public
+because we are also going to use this menu as the canvas view's
+context menu.
+
+\table
+\row
+\i The QCanvas class is used for drawing 2D vector graphics. The
+QCanvasView class is used to present a view of a canvas in an
+application's GUI. All our drawing operations take place on the
+canvas; but events (e.g. mouse clicks) take place on the canvas view.
+\endtable
+
+Each action is represented by a private slot, e.g. \c fileNew(), \c
+optionsSetData(), etc. We also have quite a number of private
+functions and data members; we'll look at all these as we go through
+the implementation.
+
+For the sake of convenience and compilation speed the chart form's
+implementation is split over three files, \c chartform.cpp for the
+GUI, \c chartform_canvas.cpp for the canvas handling and \c
+chartform_files.cpp for the file handling. We'll review each in turn.
+
+\section1 The Chart Form GUI
+
+(Extracts from \c chartform.cpp.)
+
+\quotefile chart/chartform.cpp
+\skipto file_new.xpm
+\printto file_save.xpm
+\skipto options_piechart.xpm
+\printline
+
+All the images used by \c chart have been created as \c .xpm files
+which we've placed in the \c images subdirectory.
+
+\section1 The Constructor
+
+\skipto ChartForm::ChartForm
+\printuntil QMainWindow
+\c{ ...}
+\skipto fileNewAction
+\printuntil fileSaveAction
+
+For each user action we declare a QAction pointer. Some actions are
+declared in the header file because they need to be referred to
+outside of the constructor.
+
+\table
+\row
+\i Most user actions are suitable as both menu items and as toolbar
+buttons. Qt allows us to create a single QAction which can be added to
+both a menu and a toolbar. This approach ensures that menu items and
+toolbar buttons stay in sync and saves duplicating code.
+\endtable
+
+\skipto fileNewAction
+\printuntil connect
+
+When we construct an action we give it a name, an optional icon, a
+menu text, and an accelerator short-cut key (or 0 if no accelerator is
+required). We also make it a child of the form (by passing \c this).
+When the user clicks a toolbar button or clicks a menu option the \c
+activated() signal is emitted. We connect() this signal to the
+action's slot, in the snippet shown above, to fileNew().
+
+The chart types are all mutually exclusive: you can have a pie chart
+\e or a vertical bar chart \e or a horizontal bar chart. This means
+that if the user selects the pie chart menu option, the pie chart
+toolbar button must be automatically selected too, and the other chart
+menu options and toolbar buttons must be automatically unselected.
+This behaviour is achieved by creating a QActionGroup and placing the
+chart type actions in the group.
+
+\skipto new QActionGroup
+\printuntil setExclusive
+
+The action group becomes a child of the form (\c this) and the
+exlusive behaviour is achieved by the setExclusive() call.
+
+\skipto optionsPieChartAction
+\printuntil setToggleAction
+
+Each action in the group is created in the same way as other actions,
+except that the action's parent is the group rather than the form.
+Because our chart type actions have an on/off state we call
+setToggleAction(TRUE) for each of them. Note that we do not connect
+the actions; instead, later on, we will connect the group to a slot
+that will cause the canvas to redraw.
+
+\table
+\row
+\i Why haven't we connected the group straight away? Later in the
+constructor we will read the user's options, one of which is the chart
+type. We will then set the chart type accordingly. But at that point
+we still won't have created a canvas or have any data, so all we want
+to do is toggle the canvas type toolbar buttons, but not actually draw
+the (at this point non-existent) canvas. \e After we have set the
+canvas type we will connect the group.
+\endtable
+
+Once we've created all our user actions we can create the toolbars and
+menu options that will allow the user to invoke them.
+
+\skipto new QToolBar
+\printuntil fileSaveAction
+\c{ ...}
+\skipto new QPopupMenu
+\printuntil fileSaveAction
+
+Toolbar actions and menu options are easily created from QActions.
+
+As a convenience to our users we will restore the last window position
+and size and list their recently used files. This is achieved by
+writing out their settings when the application is closed and reading
+them back when we construct the form.
+
+\skipto QSettings
+\printuntil PIE
+\skipto QFont
+\printuntil updateRecentFilesMenu
+
+The QSettings class handles user settings in a platform-independent
+way. We simply read and write settings, leaving QSettings to handle
+the platform dependencies. The insertSearchPath() call does nothing
+except under Windows so does not have to be \c{#ifdef}ed.
+
+We use readNumEntry() calls to obtain the chart form's last size and
+position, providing default values if this is the first time it has
+been run. The chart type is retrieved as an integer and cast to a
+ChartType enum value. We create a default label font and then read the
+"Font" setting, using the default we have just created if necessary.
+
+Although QSettings can handle string lists we've chosen to store each
+recently used file as a separate entry to make it easier to hand edit
+the settings. We attempt to read each possible file entry ("File1" to
+"File9"), and add each non-empty entry to the list of recently used
+files. If there are one or more recently used files we update the File
+menu by calling updateRecentFilesMenu(); (we'll review this later on).
+
+\skipto connect
+\printuntil updateChartType
+
+Now that we have set the chart type (when we read it in as a user
+setting) it is safe to connect the chart group to our
+updateChartType() slot.
+
+\skipto resize
+\printuntil move
+
+And now that we know the window size and position we can resize and
+move the chart form's window accordingly.
+
+\skipto new QCanvas
+\printuntil show
+
+We create a new QCanvas and set its size to that of the chart form
+window's client area. We also create a \c CanvasView (our own subclass
+of QCanvasView) to display the QCanvas. We make the canvas view the
+chart form's main widget and show it.
+
+\skipto isEmpty
+\printuntil drawElements
+\printline
+
+If we have a file to load we load it; otherwise we initialise our
+elements vector and draw a sample chart.
+
+\skipto statusBar
+\printline
+
+It is \e vital that we call statusBar() in the constructor, since the
+call ensures that a status bar is created for this main window.
+
+\section2 init()
+
+\skipto ::init()
+\printuntil m_elements[2]
+\c{ ...}
+
+We use an init() function because we want to initialise the canvas and
+the elements (in the \c m_elements \c ElementVector) when the form is
+constructed, and also whenever the user loads an existing data set or
+creates a new data set.
+
+We reset the caption and set the current filename to QString::null. We
+also populate the elements vector with invalid elements. This isn't
+necessary, but giving each element a different color is more
+convenient for the user since when they enter values each one will
+already have a unique color (which they can change of course).
+
+\section1 The File Handling Actions
+
+\section2 okToClear()
+
+\skipto ::okToClear()
+\printuntil return TRUE;
+\printline
+
+The okToClear() function is used to prompt the user to save their
+values if they have any unsaved data. It is used by several other
+functions.
+
+\section2 fileNew()
+
+\quotefile chart/chartform.cpp
+\skipto ::fileNew()
+\printuntil drawElements
+\printline
+\printline
+
+When the user invokes the fileNew() action we call okToClear() to give
+them the opportunity to save any unsaved data. If they either save or
+abandon or have no unsaved data we re-initialise the elements vector
+and draw the default chart.
+
+\table
+\row
+\i Should we also have invoked optionsSetData() to pop up the dialog
+through which the user can create and edit values, colors etc? You
+could try running the application as it is, and then try it having
+added a call to optionsSetData() and see which you prefer.
+\endtable
+
+\section2 fileOpen()
+
+\skipto ::fileOpen()
+\printuntil statusBar
+\printline
+
+We check that it is okToClear(). If it is we use the static
+QFileDialog::getOpenFileName() function to get the name of the file
+the user wishes to load. If we get a filename we call load().
+
+\section2 fileSaveAs()
+
+\skipto ::fileSaveAs(
+\printuntil statusBar
+\printline
+
+This function calls the static QFileDialog::getSaveFileName() to get
+the name of the file to save the data in. If the file exists we use a
+QMessageBox::warning() to notify the user and give them the option of
+abandoning the save. If the file is to be saved we update the recently
+opened files list and call fileSave() (covered in \link
+tutorial2-07.html File Handling\endlink) to perform the save.
+
+\section1 Managing a list of Recently Opened Files
+
+\quotefile chart/chartform.h
+\skipto QStringList m_recentFiles
+\printline
+
+We hold the list of recently opened files in a string list.
+
+\quotefile chart/chartform.cpp
+\skipto ::updateRecentFilesMenu()
+\printuntil SLOT
+\printline
+\printline
+\printline
+
+This function is called (usually via updateRecentFiles()) whenever the
+user opens an existing file or saves a new file. For each file in the
+string list we insert a new menu item. We prefix each filename with an
+underlined number from <u>1</u> to <u>9</u> to support keyboard access
+(e.g. \c{Alt+F, 2} to open the second file in the list). We give the
+menu item an id which is the same as the index position of the item in
+the string list, and connect each menu item to the fileOpenRecent()
+slot. The old file menu items are deleted at the same time by going
+through each possible recent file menu item id. This works because the
+other file menu items had ids created by Qt (all of which are \< 0);
+whereas the menu items we're creating all have ids \>= 0.
+
+\quotefile chart/chartform.cpp
+\skipto ::updateRecentFiles(
+\printuntil }
+
+This is called when the user opens an existing file or saves a new
+file. If the file is already in the list it simply returns. Otherwise
+the file is added to the end of the list and if the list is too large
+(\> 9 files) the first (oldest) is removed. updateRecentFilesMenu() is
+then called to recreate the list of recently used files in the File
+menu.
+
+\quotefile chart/chartform.cpp
+\skipto ::fileOpenRecent(
+\printuntil }
+
+When the user selects a recently opened file the fileOpenRecent() slot
+is called with the menu id of the file they have selected. Because we
+made the file menu ids equal to the files' index positions in the
+\c m_recentFiles list we can simply load the file indexed by the menu
+item id.
+
+\section1 Quiting
+
+\skipto ::fileQuit(
+\printuntil exit
+\printline
+\printline
+
+When the user quits we give them the opportunity to save any unsaved
+data (okToClear()) then save their options, e.g. window size and
+position, chart type, etc., before terminating.
+
+\skipto ::saveOptions(
+\printuntil }
+
+Saving the user's options using QSettings is straight-forward.
+
+\section1 Custom Dialogs
+
+We want the user to be able to set some options manually and to create
+and edit values, value colors, etc.
+
+\quotefile chart/chartform.cpp
+\skipto ::optionsSetOptions
+\printuntil setFont
+\skipto exec()
+\printto RadioButton
+\skipto drawElements
+\printuntil delete optionsForm
+\printline
+
+The form for setting options is provided by our custom \c OptionsForm
+covered in \link tutorial2-09.html Setting Options\endlink. The
+options form is a standard "dumb" dialog: we create an instance, set
+all its GUI elements to the relevant settings, and if the user clicked
+"OK" (exec() returns a true value) we read back settings from the GUI
+elements.
+
+\quotefile chart/chartform.cpp
+\skipto ::optionsSetData
+\printuntil delete setDataForm
+\printline
+
+The form for creating and editing chart data is provided by our custom
+\c SetDataForm covered in \link tutorial2-08.html Taking Data\endlink.
+This form is a "smart" dialog. We pass in the data structure we want
+to work on, and the dialog handles the presentation of the data
+structure itself. If the user clicks "OK" the dialog will update the
+data structure and exec() will return a true value. All we need to do
+in optionsSetData() if the user changed the data is mark the chart as
+changed and call drawElements() to redraw the chart with the new and
+updated data.
+
+<p align="right">
+<a href="tutorial2-04.html">&laquo; Mainly Easy</a> |
+<a href="tutorial2.html">Contents</a> |
+<a href="tutorial2-06.html">Canvas Control &raquo;</a>
+</p>
+
+*/
+
+/*!
+
+\page tutorial2-06.html
+
+\title Canvas Control
+
+We draw pie segments (or bar chart bars), and any labels, on a canvas.
+The canvas is presented to the user through a canvas view. The
+drawElements() function is called to redraw the canvas when necessary.
+
+(Extracts from \c chartform_canvas.cpp.)
+
+\section1 drawElements()
+
+\quotefile chart/chartform_canvas.cpp
+\skipto ::drawElements(
+\printuntil delete
+
+The first thing we do in drawElements() is delete all the existing
+canvas items.
+
+\skipto 16ths
+\printuntil width()
+
+Next we calculate the scale factor which depends on the type of chart
+we're going to draw.
+
+\skipto biggest
+\printto switch
+
+We will need to know how many values there are, the biggest value and
+the total value so that we can create pie segments or bars that are
+correctly scaled. We store the scaled values in the \c scales array.
+
+\printuntil }
+\printline
+
+Now that we have the necessary information we call the relevant
+drawing function, passing in the scaled values, the total and the
+count.
+
+\skipto update
+\printline
+
+Finally we update() the canvas to make the changes visible.
+
+\section2 drawHorizontalBarChart()
+
+We'll review just one of the drawing functions, to see how canvas
+items are created and placed on a canvas since this tutorial is about
+Qt rather than good (or bad) algorithms for drawing charts.
+
+\skipto ::drawHorizontalBarChart(
+\printuntil total
+\printline
+
+To draw a horizontal bar chart we need the array of scaled values, the
+total value (so that we can calculate and draw percentages if
+required) and a count of the number of values.
+
+\skipto width
+\printuntil int y
+
+We retrieve the width and height of the canvas and calculate the
+proportional height (\c proheight). We set the initial \c y position
+to 0.
+
+\skipto QPen
+\printuntil NoPen
+
+We create a pen that we will use to draw each bar (rectangle); we set
+it to \c NoPen so that no outlines are drawn.
+
+\skipto MAX_ELEMENTS
+\printuntil extent
+
+We iterate over every element in the element vector, skipping invalid
+elements. The extent of each bar (its length) is simply its scaled
+value.
+
+\printuntil show
+
+We create a new QCanvasRectangle for each bar with an x position of 0
+(since this is a horizontal bar chart every bar begins at the left), a
+y value that starts at 0 and grows by the height of each bar as each
+one is drawn, the height of the bar and the canvas that the bar should
+be drawn on. We then set the bar's brush to the color and pattern that
+the user has specified for the element, set the pen to the pen we
+created earlier (i.e. to \c NoPen) and we place the bar at position 0
+in the Z-order. Finally we call show() to draw the bar on the canvas.
+
+\printto valueLabel
+
+If the user has specified a label for the element or asked for the
+values (or percentages) to be shown, we also draw a canvas text item.
+We created our own CanvasText class (see later) because we want to
+store the corresponding element index (in the element vector) in each
+canvas text item. We extract the proportional x and y values from the
+element. If either is \< 0 then they have not been positioned by the
+user so we must calculate positions for them. We set the label's x
+value to 0 (left) and the y value to the top of the bar (so that the
+label's top-left will be at this x, y position).
+
+\printline
+
+We then call a helper function valueLabel() which returns a string
+containing the label text. (The valueLabel() function adds on the
+value or percentage to the textual label if the user has set the
+appropriate options.)
+
+\printuntil setProY
+
+We then create a CanvasText item, passing it the index of this element
+in the element vector, and the label, font and canvas to use. We set
+the text item's text color to the color specified by the user and set
+the item's x and y positions proportional to the canvas's width and
+height. We set the Z-order to 1 so that the text item will always be
+above (in front of) the bar (whose Z-order is 0). We call show() to
+draw the text item on the canvas, and set the element's relative x and
+y positions.
+
+\printuntil proheight
+
+After drawing a bar and possibly its label, we increment y by the
+proportional height ready to draw the next element.
+
+\printline
+\printline
+\printline
+
+\section1 Subclassing QCanvasText
+
+(Extracts from \c canvastext.h.)
+
+\quotefile chart/canvastext.h
+\skipto public QCanvasText
+\printuntil private
+\printuntil };
+
+Our CanvasText subclass is a very simple specialisation of
+QCanvasText. All we've done is added a single private member \c
+m_index which holds the element vector index of the element associated
+with this text item, and provided a getter and setter for this value.
+
+\section1 Subclassing QCanvasView
+
+(Extracts from \c canvasview.h.)
+
+\quotefile chart/canvasview.h
+\skipto public QCanvasView
+\printuntil private
+\printuntil };
+
+We need to subclass QCanvasView so that we can handle:
+\list 1
+\i Context menu requests.
+\i Form resizing.
+\i Users dragging labels to arbitrary positions.
+\endlist
+
+To support these we store a pointer to the canvas item that is being
+moved and its last position. We also store a pointer to the element
+vector.
+
+\section2 Supporting Context Menus
+
+(Extracts from \c canvasview.cpp.)
+
+\quotefile chart/canvasview.cpp
+\skipto ::contentsContextMenuEvent
+\printuntil }
+
+When the user invokes a context menu (e.g. by right-clicking on most
+platforms) we cast the canvas view's parent (which is the chart form)
+to the right type and then exec()ute the options menu at the cursor
+position.
+
+\section2 Handling Resizing
+
+\skipto ::viewportResizeEvent
+\printuntil }
+
+To resize we simply resize the canvas that the canvas view is
+presenting to the width and height of the form's client area, then
+call drawElements() to redraw the chart. Because drawElements() draws
+everything relative to the canvas's width and height the chart is
+drawn correctly.
+
+\section2 Dragging Labels into Position
+
+When the user wants to drag a label into position they click it, then
+drag and release at the new position.
+
+\skipto ::contentsMousePressEvent
+\printuntil return
+\printuntil movingItem
+\printuntil }
+
+When the user clicks the mouse we create a list of canvas items that
+the mouse click "collided" with (if any). We then iterate over this
+list and if we find a \c CanvasText item we set it as the moving item
+and record its position. Otherwise we set there to be no moving item.
+
+\skipto ::contentsMouseMoveEvent
+\printuntil update
+\printuntil }
+\printuntil }
+
+As the user drags the mouse, move events are generated. If there is a
+moving item we calculate the offset from the last mouse position and
+move the item by this offset amount. We record the new position as the
+last position. Because the chart has now changed we call setChanged()
+so that the user will be prompted to save if they attempt to exit or
+to load an existing chart or to create a new chart. We also update the
+element's proportional x and y positions for the current chart type to
+the current x and y positions proportional to the width and height
+respectively. We know which element to update because when we create
+each canvas text item we pass it the index position of the element it
+corresponds to. We subclassed QCanvasText so that we could set and get
+this index value. Finally we call update() to make the canvas redraw.
+
+\table
+\row
+\i A QCanvas has no visual representation. To see the contents of a
+canvas you must create a QCanvasView to present the canvas. Items only
+appear in the canvas view if they have been show()n, and then, only if
+QCanvas::update() has been called. By default a QCanvas's background
+color is white, and by default shapes drawn on the canvas, e.g.
+QCanvasRectangle, QCanvasEllipse, etc., have their fill color set to
+white, so setting a non-white brush color is highly recommended!
+\endtable
+
+<p align="right">
+<a href="tutorial2-05.html">&laquo; Presenting the GUI</a> |
+<a href="tutorial2.html">Contents</a> |
+<a href="tutorial2-07.html">File Handling &raquo;</a>
+</p>
+
+*/
+
+/*!
+
+\page tutorial2-07.html
+
+\title File Handling
+
+(Extracts from \c chartform_files.cpp.)
+
+\section1 Reading Chart Data
+
+\quotefile chart/chartform_files.cpp
+\skipto ::load(
+\printuntil isValid
+\printline
+\skipto file.close
+\printline
+
+\skipto setCaption
+\printuntil }
+
+Loading a data set is very easy. We open the file and create a text
+stream. While there's data to read we stream an element into \c
+element and if it is valid we insert it into the \c m_elements vector.
+All the detail is handled by the \c Element class. Then we close
+the file and update the caption and the recent files list. Finally we
+draw the chart and mark it as unchanged.
+
+\section1 Writing Chart Data
+
+\skipto ::fileSave
+\printline
+\printline
+\skipto QFile
+\printuntil FALSE
+\printline
+
+Saving data is equally easy. We open the file and create a text
+stream. We then stream every valid element to the text stream. All the
+detail is handled by the \c Element class.
+
+<p align="right">
+<a href="tutorial2-06.html">&laquo; Canvas Control</a> |
+<a href="tutorial2.html">Contents</a> |
+<a href="tutorial2-08.html">Taking Data &raquo;</a>
+</p>
+
+*/
+
+/*!
+
+\page tutorial2-08.html
+
+\title Taking Data
+
+\img chart-setdata.png The set data dialog
+
+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.
+
+(Extracts from \c setdataform.h.)
+
+\quotefile chart/setdataform.h
+\skipto public QDialog
+\printuntil };
+
+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.
+
+(Extracts from \c setdataform.cpp.)
+
+\quotefile chart/setdataform.cpp
+\skipto pattern01
+\printuntil pattern02
+
+We have created a small \c .XPM image to show each brush pattern that
+Qt supports. We'll use these in the pattern combobox.
+
+\section1 The Constructor
+
+\skipto SetDataForm::SetDataForm
+\printuntil m_decimalPlaces
+
+We pass most of the arguments to the QDialog 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.
+
+\skipto setCaption
+\printuntil resize
+
+We set a caption for the dialog and resize it.
+
+\skipto tableButtonBox
+\printline
+
+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.
+
+\skipto QTable
+\printuntil addWidget
+
+We create a new QTable 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.
+
+\skipto QHBoxLayout
+\printline
+
+We create a horizontal box layout to hold the buttons.
+
+\skipto QPushButton
+\printuntil addWidget
+
+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.
+
+\skipto QSpacerItem
+\printuntil addItem
+
+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.
+
+
+\skipto QPushButton
+\printuntil addWidget( cancelPushButton
+
+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 \c Esc
+key an accelerator for the Cancel button.
+
+\skipto addLayout
+\printline
+
+We add the buttonBox layout to the tableButtonBox and the layout is
+complete.
+
+\skipto connect
+\printuntil reject
+
+We now "wire up" the form.
+\list
+\i 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.
+\i We connect the QTable'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.
+\i 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.
+\i If the user clicks the Color button we call a setColor() slot.
+\i The OK button is connected to the accept() slot; we will update the
+elements vector in this slot.
+\i The Cancel button is connected to the QDialog reject() slot, and
+requires no further code or action on our part.
+\endlist
+
+\skipto QPixmap
+\printline
+\printline
+\printline
+
+We create a pixmap for every brush pattern and store them in the \c
+patterns array.
+
+\skipto QRect
+\printline
+\printline
+
+We obtain the rectangle that will be occupied by each color cell and
+create a blank pixmap of that size.
+
+\skipto ChartForm::MAX_ELEMENTS
+\printuntil labelColor
+\printuntil setText
+
+For each element in the element vector we must populate the table.
+
+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.
+
+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 QTableItem (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.
+
+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. QTable can make use of QComboTableItem
+items; but these only support text, so we use setCellWidget() to
+insert \l{QComboBox}'s into the table instead.
+
+Next we insert the element's label. Finally we set the label color in
+the same way as we set the value color.
+
+\section1 The Slots
+
+\skipto ::currentChanged(
+\printuntil setEnabled
+\printline
+
+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.
+
+\skipto ::valueChanged(
+\printuntil ?
+\printline
+\printline
+
+If the user changes the value we must format it using the correct
+number of decimal places, or indicate that it is invalid.
+
+\skipto ::setColor(
+\printuntil }
+
+If the user presses the Color button we call the other setColor()
+function and put the focus back into the table.
+
+\skipto ::setColor(
+\printuntil setText
+\printline
+\printline
+
+If this function is called with the focus on a color cell we call
+the static QColorDialog::getColor() 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.
+
+\skipto ::accept(
+\printuntil QDialog
+\printline
+
+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 \c INVALID if the value is invalid. We set the value color
+and the label color by constructing QColor 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).
+
+Finally we call QDialog::accept().
+
+<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>
+
+*/
+
+/*!
+
+\page tutorial2-09.html
+
+\title Setting Options
+
+\img chart-options.png The options dialog
+
+We provide an options dialog so that the user can set options that
+apply to all data sets in one place.
+
+(Extracts from \c optionsform.h.)
+
+\quotefile chart/optionsform.h
+\skipto public QDialog
+\printuntil };
+
+The layout of this dialog is slightly more complicated than for the
+set data form, but we only need a single slot. Unlike the "smart" set
+data form this is a "dumb" dialog that simply provides the widgets for
+the caller to set and read. The caller is responsible for updating
+things based on the changes the user makes.
+
+(Extracts from \c optionsform.cpp.)
+
+\quotefile chart/optionsform.cpp
+\skipto options_horizontalbarchart
+\printline
+\printline
+\printline
+
+We include some some pixmaps to use in the chart type combobox.
+
+\section1 The Constructor
+
+\skipto OptionsForm::OptionsForm
+\printuntil resize
+
+We pass all the arguments on to the QDialog constructor, set a caption
+and set an initial size.
+
+The layout of the form will be to have the chart type label and combo
+box in a horizontal box layout, and similarly for the font button and
+font label, and for the decimal places label and spinbox. The
+buttons will also be placed in a horizontal layout, but with a spacer
+to move them to the right. The show values radio buttons will be
+vertically aligned within a frame. All of these will be laid out in a
+vertical box layout.
+
+\skipto optionsFormLayout
+\printline
+
+All the widgets will be laid out within the form's vertical box layout.
+
+\skipto chartTypeLayout
+\printline
+
+The chart type label and combobox will be laid out side by side.
+
+\skipto QLabel
+\printuntil addLayout
+
+We create the chart type label (with an accelerator which we'll relate
+to the chart type combobox later). We also create a chart type
+combobox, populating it with both pixmaps and text. We add them both
+to the horizontal layout and add the horizontal layout to the form's
+vertical layout.
+
+\skipto fontLayout
+\printuntil addLayout
+
+We create a horizontal box layout to hold the font button and font
+label. The font button is straight-forward. We add a spacer to improve
+the appearance. The font text label is initially empty (since we don't
+know what font the user is using).
+
+\skipto QFrame
+\printuntil addWidget( addValuesButtonGroup
+
+The user may opt to display their own labels as they are or to add the
+values at the end of each label, either as-is or as percentages.
+
+We create a frame to present the radio buttons in and create a layout
+for them. We create a button group (so that Qt will take care of
+handling the exclusive radio button behaviour automatically). Next we
+create the radio buttons, making "No" the default.
+
+The decimal places label and spin box are laid out just like the other
+horizontal layouts, and the buttons are laid out in a very similar way
+to the buttons in the set data form.
+
+\skipto connect
+\printuntil reject
+
+We only need three connections:
+\list 1
+\i When the user clicks the font button we execute our own
+chooseFont() slot.
+\i If the user clicks OK we call QDialog::accept(); it is up to the
+caller to read the data from the dialog's widgets and perform any
+necessary actions.
+\i If the user clicks Cancel we call QDialog::reject().
+\endlist
+
+\skipto setBuddy
+\printline
+\printline
+
+We use the setBuddy() function to associate widgets with label
+accelerators.
+
+\section1 The Slots
+
+\skipto ::chooseFont
+\printuntil }
+
+When the user clicks the Font button this slot is invoked. It simply
+calls the static QFontDialog::getFont() function to obtain the user's
+choice of font. If they chose a font we call our setFont() slot which
+will present a textual description of the font in the font label.
+
+\skipto ::setFont
+\printuntil }
+
+This function displays a textual description of the chosen font in the
+font label and holds a copy of the font in the \c m_font member. We
+need the font in a member so that we can provide a default font for
+chooseFont().
+
+<p align="right">
+<a href="tutorial2-08.html">&laquo; Taking Data</a> |
+<a href="tutorial2.html">Contents</a> |
+<a href="tutorial2-10.html">The Project File &raquo;</a>
+</p>
+
+*/
+
+/*!
+
+\page tutorial2-10.html
+
+\title The Project File
+
+(\c chart.pro.)
+
+\quotefile chart/chart.pro
+\printuntil main.cpp
+
+By using a project file we can insulate ourselves from having to
+create Makefiles for the platforms we wish to target. To generate a
+Makefile all we need do is run \link qmake-manual.book qmake\endlink,
+e.g.
+\code
+qmake -o Makefile chart.pro
+\endcode
+
+<p align="right">
+<a href="tutorial2-09.html">&laquo; Setting Options</a> |
+<a href="tutorial2.html">Contents</a> |
+<a href="tutorial2-11.html">Wrapping Up &raquo;</a>
+</p>
+
+*/
+
+/*!
+
+\page tutorial2-11.html
+
+\title Wrapping Up
+
+The \c chart application shows how straight-forward it is to create
+applications and their dialogs with Qt. Creating menus and toolbars is
+easy and Qt's \link signalsandslots.html signals and slots\endlink
+mechanism considerably simplifies GUI event handling.
+
+Manually creating layouts can take some time to master, but there is
+an easy alternative: \link designer-manual.book Qt&nbsp;Designer\endlink.
+\link designer-manual.book Qt&nbsp;Designer\endlink includes simple but
+powerful layout tools and a code editor. It can automatically generate
+\c main.cpp and the \c .pro project file.
+
+The \c chart application is ripe for further development and
+experimentation. You might consider implementing some of the following
+ideas:
+\list
+\i Use a QValidator subclass to ensure that only valid doubles are
+entered as values.
+\i Adding more chart types, e.g. line graph, area graph and hi-lo
+graph.
+\i Allowing the user to set top, bottom left and right margins.
+\i Allowing the user to specify a title which they can drag into
+position like the labels.
+\i Providing an axis drawing and labelling option.
+\i Providing an option to provide a key (or legend) instead of labels.
+\i Adding a 3D look option to all chart types.
+\endlist
+
+<p align="right">
+<a href="tutorial2-10.html">&laquo; The Project File</a> |
+<a href="tutorial2.html">Contents &raquo;</a>
+</p>
+
+*/