<!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:349 -->
< html >
< head >
< meta http-equiv = "Content-Type" content = "text/html; charset=ISO-8859-1" >
< title > Presenting the GUI< / 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 > Presenting the GUI< / h1 >
< p >
< p > < center > < img src = "chart-main2.png" alt = "The chart application" > < / center >
< p > The < tt > chart< / tt > application provides access to options via menus and
toolbar buttons arranged around a central widget, a CanvasView, in a
conventional document-centric style.
< p > (Extracts from < tt > chartform.h< / tt > .)
< p >
< pre > class ChartForm: public < a href = "tqmainwindow.html" > TQMainWindow< / a >
{
< a href = "metaobjects.html#TQ_OBJECT" > TQ_OBJECT< / a >
public:
enum { MAX_ELEMENTS = 100 };
enum { MAX_RECENTFILES = 9 }; // Must not exceed 9
enum ChartType { PIE, VERTICAL_BAR, HORIZONTAL_BAR };
enum AddValuesType { NO, YES, AS_PERCENTAGE };
ChartForm( const < a href = "tqstring.html" > TQString< / a > & filename );
~ChartForm();
int chartType() { return m_chartType; }
void setChanged( bool changed = TRUE ) { m_changed = changed; }
void drawElements();
< a href = "tqpopupmenu.html" > TQPopupMenu< / a > *optionsMenu; // Why public? See canvasview.cpp
protected:
virtual void closeEvent( < a href = "qcloseevent.html" > TQCloseEvent< / a > * );
private slots:
void fileNew();
void fileOpen();
void fileOpenRecent( int index );
void fileSave();
void fileSaveAs();
void fileSaveAsPixmap();
void filePrint();
void fileQuit();
void optionsSetData();
void updateChartType( < a href = "tqaction.html" > TQAction< / a > *action );
void optionsSetFont();
void optionsSetOptions();
void helpHelp();
void helpAbout();
void helpAboutTQt();
void saveOptions();
private:
void init();
void load( const < a href = "tqstring.html" > TQString< / a > & filename );
bool okToClear();
void drawPieChart( const double scales[], double total, int count );
void drawVerticalBarChart( const double scales[], double total, int count );
void drawHorizontalBarChart( const double scales[], double total, int count );
< a href = "tqstring.html" > TQString< / a > valueLabel( const < a href = "tqstring.html" > TQString< / a > & label, double value, double total );
void updateRecentFiles( const < a href = "tqstring.html" > TQString< / a > & filename );
void updateRecentFilesMenu();
void setChartType( ChartType chartType );
< a href = "tqpopupmenu.html" > TQPopupMenu< / a > *fileMenu;
< a href = "tqaction.html" > TQAction< / a > *optionsPieChartAction;
< a href = "tqaction.html" > TQAction< / a > *optionsHorizontalBarChartAction;
< a href = "tqaction.html" > TQAction< / a > *optionsVerticalBarChartAction;
< a href = "tqstring.html" > TQString< / a > m_filename;
< a href = "tqstringlist.html" > TQStringList< / a > m_recentFiles;
< a href = "tqcanvas.html" > TQCanvas< / a > *m_canvas;
CanvasView *m_canvasView;
bool m_changed;
ElementVector m_elements;
< a href = "tqprinter.html" > TQPrinter< / a > *m_printer;
ChartType m_chartType;
AddValuesType m_addValues;
int m_decimalPlaces;
< a href = "tqfont.html" > TQFont< / a > m_font;
};
< / pre >
< p > We create a < tt > ChartForm< / tt > subclass of < a href = "tqmainwindow.html" > TQMainWindow< / a > . Our subclass uses
the < a href = "metaobjects.html#TQ_OBJECT" > TQ_OBJECT< / a > macro to support TQt's < a href = "signalsandslots.html" > signals and slots< / a > mechanism.
< p > 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.
< p > < center > < table cellpadding = "4" cellspacing = "2" border = "0" >
< tr bgcolor = "#f0f0f0" >
< td valign = "top" > The < a href = "tqcanvas.html" > TQCanvas< / a > class is used for drawing 2D vector graphics. The
< a href = "tqcanvasview.html" > TQCanvasView< / a > 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.
< / table > < / center >
< p > Each action is represented by a private slot, e.g. < tt > fileNew()< / tt > , < tt > optionsSetData()< / tt > , 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.
< p > For the sake of convenience and compilation speed the chart form's
implementation is split over three files, < tt > chartform.cpp< / tt > for the
GUI, < tt > chartform_canvas.cpp< / tt > for the canvas handling and < tt > chartform_files.cpp< / tt > for the file handling. We'll review each in turn.
< p > < h2 > The Chart Form GUI
< / h2 >
< a name = "1" > < / a > < p > (Extracts from < tt > chartform.cpp< / tt > .)
< p >
< pre > #include "images/file_new.xpm"
#include "images/file_open.xpm"
< / pre > < pre > #include "images/options_piechart.xpm"
< / pre >
< p > All the images used by < tt > chart< / tt > have been created as < tt > .xpm< / tt > files
which we've placed in the < tt > images< / tt > subdirectory.
< p > < h2 > The Constructor
< / h2 >
< a name = "2" > < / a > < p > < pre > ChartForm::ChartForm( const < a href = "tqstring.html" > TQString< / a > & filename )
: < a href = "tqmainwindow.html" > TQMainWindow< / a > ( 0, 0, WDestructiveClose )
< / pre > < tt > ...< / tt >
< pre > < a href = "tqaction.html" > TQAction< / a > *fileNewAction;
< a href = "tqaction.html" > TQAction< / a > *fileOpenAction;
< a href = "tqaction.html" > TQAction< / a > *fileSaveAction;
< / pre >
< p > For each user action we declare a < a href = "tqaction.html" > TQAction< / a > pointer. Some actions are
declared in the header file because they need to be referred to
outside of the constructor.
< p > < center > < table cellpadding = "4" cellspacing = "2" border = "0" >
< tr bgcolor = "#d0d0d0" >
< td valign = "top" > Most user actions are suitable as both menu items and as toolbar
buttons. TQt allows us to create a single TQAction 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.
< / table > < / center >
< p > < pre > fileNewAction = new < a href = "tqaction.html" > TQAction< / a > (
"New Chart", TQPixmap( file_new ),
"& New", CTRL+Key_N, this, "new" );
< a href = "tqobject.html#connect" > connect< / a > ( fileNewAction, TQ_SIGNAL( < a href = "tqaction.html#activated" > activated< / a > () ), this, TQ_SLOT( fileNew() ) );
< / pre >
< p > 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 < tt > this< / tt > ).
When the user clicks a toolbar button or clicks a menu option the < tt > activated()< / tt > signal is emitted. We connect() this signal to the
action's slot, in the snippet shown above, to fileNew().
< p > The chart types are all mutually exclusive: you can have a pie chart
< em > or< / em > a vertical bar chart < em > or< / em > 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 < a href = "tqactiongroup.html" > TQActionGroup< / a > and placing the
chart type actions in the group.
< p > < pre > < a href = "tqactiongroup.html" > TQActionGroup< / a > *chartGroup = new < a href = "tqactiongroup.html" > TQActionGroup< / a > ( this ); // Connected later
chartGroup-> < a href = "tqactiongroup.html#setExclusive" > setExclusive< / a > ( TRUE );
< / pre >
< p > The action group becomes a child of the form (< tt > this< / tt > ) and the
exlusive behaviour is achieved by the setExclusive() call.
< p > < pre > optionsPieChartAction = new < a href = "tqaction.html" > TQAction< / a > (
"Pie Chart", TQPixmap( options_piechart ),
"& Pie Chart", CTRL+Key_I, chartGroup, "pie chart" );
optionsPieChartAction-> < a href = "tqaction.html#setToggleAction" > setToggleAction< / a > ( TRUE );
< / pre >
< p > 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.
< p > < center > < table cellpadding = "4" cellspacing = "2" border = "0" >
< tr bgcolor = "#f0f0f0" >
< td valign = "top" > 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. < em > After< / em > we have set the
canvas type we will connect the group.
< / table > < / center >
< p > Once we've created all our user actions we can create the toolbars and
menu options that will allow the user to invoke them.
< p > < pre > < a href = "tqtoolbar.html" > TQToolBar< / a > * fileTools = new < a href = "tqtoolbar.html" > TQToolBar< / a > ( this, "file operations" );
fileTools-> < a href = "tqtoolbar.html#setLabel" > setLabel< / a > ( "File Operations" );
fileNewAction-> < a href = "tqaction.html#addTo" > addTo< / a > ( fileTools );
fileOpenAction-> < a href = "tqaction.html#addTo" > addTo< / a > ( fileTools );
fileSaveAction-> < a href = "tqaction.html#addTo" > addTo< / a > ( fileTools );
< / pre > < tt > ...< / tt >
< pre > fileMenu = new < a href = "tqpopupmenu.html" > TQPopupMenu< / a > ( this );
< a href = "tqmainwindow.html#menuBar" > menuBar< / a > ()-> insertItem( "& File", fileMenu );
fileNewAction-> < a href = "tqaction.html#addTo" > addTo< / a > ( fileMenu );
fileOpenAction-> < a href = "tqaction.html#addTo" > addTo< / a > ( fileMenu );
fileSaveAction-> < a href = "tqaction.html#addTo" > addTo< / a > ( fileMenu );
< / pre >
< p > Toolbar actions and menu options are easily created from TQActions.
< p > 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.
< p > < pre > < a href = "tqsettings.html" > TQSettings< / a > settings;
settings.< a href = "tqsettings.html#insertSearchPath" > insertSearchPath< / a > ( TQSettings::Windows, WINDOWS_REGISTRY );
int windowWidth = settings.< a href = "tqsettings.html#readNumEntry" > readNumEntry< / a > ( APP_KEY + "WindowWidth", 460 );
int windowHeight = settings.< a href = "tqsettings.html#readNumEntry" > readNumEntry< / a > ( APP_KEY + "WindowHeight", 530 );
int windowX = settings.< a href = "tqsettings.html#readNumEntry" > readNumEntry< / a > ( APP_KEY + "WindowX", -1 );
int windowY = settings.< a href = "tqsettings.html#readNumEntry" > readNumEntry< / a > ( APP_KEY + "WindowY", -1 );
setChartType( ChartType(
settings.< a href = "tqsettings.html#readNumEntry" > readNumEntry< / a > ( APP_KEY + "ChartType", int(PIE) ) ) );
< / pre > < pre > m_font = TQFont( "Helvetica", 18, TQFont::Bold );
m_font.fromString(
settings.< a href = "tqsettings.html#readEntry" > readEntry< / a > ( APP_KEY + "Font", m_font.toString() ) );
for ( int i = 0; i < MAX_RECENTFILES; ++i ) {
< a href = "tqstring.html" > TQString< / a > filename = settings.< a href = "tqsettings.html#readEntry" > readEntry< / a > ( APP_KEY + "File" +
TQString::< a href = "tqstring.html#number" > number< / a > ( i + 1 ) );
if ( !filename.< a href = "tqstring.html#isEmpty" > isEmpty< / a > () )
m_recentFiles.push_back( filename );
}
if ( m_recentFiles.count() )
updateRecentFilesMenu();
< / pre >
< p > The < a href = "tqsettings.html" > TQSettings< / a > class handles user settings in a platform-independent
way. We simply read and write settings, leaving TQSettings to handle
the platform dependencies. The insertSearchPath() call does nothing
except under Windows so does not have to be < tt > #ifdef< / tt > ed.
< p > 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.
< p > Although TQSettings 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).
< p > < pre > < a href = "tqobject.html#connect" > connect< / a > ( chartGroup, TQ_SIGNAL( < a href = "tqactiongroup.html#selected" > selected< / a > (TQAction*) ),
this, TQ_SLOT( updateChartType(TQAction*) ) );
< / pre >
< p > 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.
< p > < pre > < a href = "tqwidget.html#resize" > resize< / a > ( windowWidth, windowHeight );
if ( windowX != -1 || windowY != -1 )
< a href = "tqwidget.html#move" > move< / a > ( windowX, windowY );
< / pre >
< p > And now that we know the window size and position we can resize and
move the chart form's window accordingly.
< p > < pre > m_canvas = new < a href = "tqcanvas.html" > TQCanvas< / a > ( this );
m_canvas-> < a href = "tqcanvas.html#resize" > resize< / a > ( < a href = "tqwidget.html#width" > width< / a > (), height() );
m_canvasView = new CanvasView( m_canvas, & m_elements, this );
< a href = "tqmainwindow.html#setCentralWidget" > setCentralWidget< / a > ( m_canvasView );
m_canvasView-> < a href = "tqwidget.html#show" > show< / a > ();
< / pre >
< p > We create a new < a href = "tqcanvas.html" > TQCanvas< / a > and set its size to that of the chart form
window's client area. We also create a < tt > CanvasView< / tt > (our own subclass
of < a href = "tqcanvasview.html" > TQCanvasView< / a > ) to display the TQCanvas. We make the canvas view the
chart form's main widget and show it.
< p > < pre > if ( !filename.< a href = "tqstring.html#isEmpty" > isEmpty< / a > () )
load( filename );
else {
init();
m_elements[0].set( 20, red, 14, "Red" );
m_elements[1].set( 70, cyan, 2, "Cyan", darkGreen );
m_elements[2].set( 35, blue, 11, "Blue" );
m_elements[3].set( 55, yellow, 1, "Yellow", darkBlue );
m_elements[4].set( 80, magenta, 1, "Magenta" );
drawElements();
}
< / pre >
< p > If we have a file to load we load it; otherwise we initialise our
elements vector and draw a sample chart.
< p > < pre > < a href = "tqmainwindow.html#statusBar" > statusBar< / a > ()-> message( "Ready", 2000 );
< / pre >
< p > It is < em > vital< / em > that we call statusBar() in the constructor, since the
call ensures that a status bar is created for this main window.
< p > < h3 > init()
< / h3 >
< a name = "2-1" > < / a > < p > < pre > void ChartForm::init()
{
< a href = "tqwidget.html#setCaption" > setCaption< / a > ( "Chart" );
m_filename = < a href = "tqstring.html#TQString-null" > TQString::null< / a > ;
m_changed = FALSE;
m_elements[0] = Element( Element::INVALID, red );
m_elements[1] = Element( Element::INVALID, cyan );
m_elements[2] = Element( Element::INVALID, blue );
< / pre > < tt > ...< / tt >
< p > We use an init() function because we want to initialise the canvas and
the elements (in the < tt > m_elements< / tt > < tt > ElementVector< / tt > ) when the form is
constructed, and also whenever the user loads an existing data set or
creates a new data set.
< p > We reset the caption and set the current filename to TQString::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).
< p > < h2 > The File Handling Actions
< / h2 >
< a name = "3" > < / a > < p > < h3 > okToClear()
< / h3 >
< a name = "3-1" > < / a > < p > < pre > bool ChartForm::okToClear()
{
if ( m_changed ) {
< a href = "tqstring.html" > TQString< / a > msg;
if ( m_filename.isEmpty() )
msg = "Unnamed chart ";
else
msg = TQString( "Chart '%1'\n" ).arg( m_filename );
msg += "has been changed.";
int x = TQMessageBox::< a href = "ntqmessagebox.html#information" > information< / a > ( this, "Chart -- Unsaved Changes",
msg, "& Save", "Cancel", "& Abandon",
0, 1 );
switch( x ) {
case 0: // Save
fileSave();
break;
case 1: // Cancel
default:
return FALSE;
case 2: // Abandon
break;
}
}
return TRUE;
}
< / pre >
< p > 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.
< p > < h3 > fileNew()
< / h3 >
< a name = "3-2" > < / a > < p >
< pre > void ChartForm::fileNew()
{
if ( okToClear() ) {
init();
drawElements();
}
}
< / pre >
< p > 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.
< p > < center > < table cellpadding = "4" cellspacing = "2" border = "0" >
< tr bgcolor = "#d0d0d0" >
< td valign = "top" > 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.
< / table > < / center >
< p > < h3 > fileOpen()
< / h3 >
< a name = "3-3" > < / a > < p > < pre > void ChartForm::fileOpen()
{
if ( !okToClear() )
return;
< a href = "tqstring.html" > TQString< / a > filename = TQFileDialog::< a href = "tqfiledialog.html#getOpenFileName" > getOpenFileName< / a > (
TQString::null, "Charts (*.cht)", this,
"file open", "Chart -- File Open" );
< a name = "x2567" > < / a > if ( !filename.< a href = "tqstring.html#isEmpty" > isEmpty< / a > () )
load( filename );
else
< a href = "tqmainwindow.html#statusBar" > statusBar< / a > ()-> message( "File Open abandoned", 2000 );
}
< / pre >
< p > We check that it is okToClear(). If it is we use the static
< a href = "tqfiledialog.html#getOpenFileName" > TQFileDialog::getOpenFileName< / a > () function to get the name of the file
the user wishes to load. If we get a filename we call load().
< p > < h3 > fileSaveAs()
< / h3 >
< a name = "3-4" > < / a > < p > < pre > void ChartForm::fileSaveAs()
{
< a href = "tqstring.html" > TQString< / a > filename = TQFileDialog::< a href = "tqfiledialog.html#getSaveFileName" > getSaveFileName< / a > (
TQString::null, "Charts (*.cht)", this,
"file save as", "Chart -- File Save As" );
if ( !filename.< a href = "tqstring.html#isEmpty" > isEmpty< / a > () ) {
int answer = 0;
< a name = "x2563" > < / a > if ( TQFile::< a href = "tqfile.html#exists" > exists< / a > ( filename ) )
< a name = "x2566" > < / a > answer = TQMessageBox::< a href = "ntqmessagebox.html#warning" > warning< / a > (
this, "Chart -- Overwrite File",
TQString( "Overwrite\n\'%1\'?" ).
arg( filename ),
"& Yes", "& No", TQString::null, 1, 1 );
if ( answer == 0 ) {
m_filename = filename;
updateRecentFiles( filename );
fileSave();
return;
}
}
< a href = "tqmainwindow.html#statusBar" > statusBar< / a > ()-> message( "Saving abandoned", 2000 );
}
< / pre >
< p > This function calls the static < a href = "tqfiledialog.html#getSaveFileName" > TQFileDialog::getSaveFileName< / a > () to get
the name of the file to save the data in. If the file exists we use a
< a href = "ntqmessagebox.html#warning" > TQMessageBox::warning< / a > () 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 < a href = "tutorial2-07.html" > File Handling< / a > ) to perform the save.
< p > < h2 > Managing a list of Recently Opened Files
< / h2 >
< a name = "4" > < / a > < p >
< pre > < a href = "tqstringlist.html" > TQStringList< / a > m_recentFiles;
< / pre >
< p > We hold the list of recently opened files in a string list.
< p >
< pre > void ChartForm::updateRecentFilesMenu()
{
for ( int i = 0; i < MAX_RECENTFILES; ++i ) {
if ( fileMenu-> < a href = "tqmenudata.html#findItem" > findItem< / a > ( i ) )
fileMenu-> < a href = "tqmenudata.html#removeItem" > removeItem< / a > ( i );
if ( i < int(m_recentFiles.count()) )
fileMenu-> < a href = "tqmenudata.html#insertItem" > insertItem< / a > ( TQString( "& %1 %2" ).
arg( i + 1 ).arg( m_recentFiles[i] ),
this, TQ_SLOT( fileOpenRecent(int) ),
0, i );
}
}
< / pre >
< p > 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. < tt > Alt+F, 2< / tt > 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 TQt (all of which are < 0);
whereas the menu items we're creating all have ids > = 0.
< p >
< pre > void ChartForm::updateRecentFiles( const < a href = "tqstring.html" > TQString< / a > & filename )
{
if ( m_recentFiles.find( filename ) != m_recentFiles.end() )
return;
m_recentFiles.push_back( filename );
if ( m_recentFiles.count() > MAX_RECENTFILES )
m_recentFiles.pop_front();
updateRecentFilesMenu();
}
< / pre >
< p > 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.
< p >
< pre > void ChartForm::fileOpenRecent( int index )
{
if ( !okToClear() )
return;
load( m_recentFiles[index] );
}
< / pre >
< p > 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
< tt > m_recentFiles< / tt > list we can simply load the file indexed by the menu
item id.
< p > < h2 > Quiting
< / h2 >
< a name = "5" > < / a > < p > < pre > void ChartForm::fileQuit()
{
if ( okToClear() ) {
saveOptions();
tqApp-> < a href = "ntqapplication.html#exit" > exit< / a > ( 0 );
}
}
< / pre >
< p > 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.
< p > < pre > void ChartForm::saveOptions()
{
< a href = "tqsettings.html" > TQSettings< / a > settings;
settings.< a href = "tqsettings.html#insertSearchPath" > insertSearchPath< / a > ( TQSettings::Windows, WINDOWS_REGISTRY );
settings.< a href = "tqsettings.html#writeEntry" > writeEntry< / a > ( APP_KEY + "WindowWidth", width() );
settings.< a href = "tqsettings.html#writeEntry" > writeEntry< / a > ( APP_KEY + "WindowHeight", height() );
settings.< a href = "tqsettings.html#writeEntry" > writeEntry< / a > ( APP_KEY + "WindowX", x() );
settings.< a href = "tqsettings.html#writeEntry" > writeEntry< / a > ( APP_KEY + "WindowY", y() );
settings.< a href = "tqsettings.html#writeEntry" > writeEntry< / a > ( APP_KEY + "ChartType", int(m_chartType) );
settings.< a href = "tqsettings.html#writeEntry" > writeEntry< / a > ( APP_KEY + "AddValues", int(m_addValues) );
settings.< a href = "tqsettings.html#writeEntry" > writeEntry< / a > ( APP_KEY + "Decimals", m_decimalPlaces );
settings.< a href = "tqsettings.html#writeEntry" > writeEntry< / a > ( APP_KEY + "Font", m_font.toString() );
for ( int i = 0; i < int(m_recentFiles.count()); ++i )
settings.< a href = "tqsettings.html#writeEntry" > writeEntry< / a > ( APP_KEY + "File" + TQString::number( i + 1 ),
m_recentFiles[i] );
}
< / pre >
< p > Saving the user's options using < a href = "tqsettings.html" > TQSettings< / a > is straight-forward.
< p > < h2 > Custom Dialogs
< / h2 >
< a name = "6" > < / a > < p > We want the user to be able to set some options manually and to create
and edit values, value colors, etc.
< p >
< pre > void ChartForm::optionsSetOptions()
{
OptionsForm *optionsForm = new OptionsForm( this );
optionsForm-> chartTypeComboBox-> setCurrentItem( m_chartType );
optionsForm-> < a href = "tqwidget.html#setFont" > setFont< / a > ( m_font );
< / pre > < pre > if ( optionsForm-> < a href = "ntqdialog.html#exec" > exec< / a > () ) {
setChartType( ChartType(
optionsForm-> chartTypeComboBox-> currentItem()) );
m_font = optionsForm-> < a href = "tqwidget.html#font" > font< / a > ();
< / pre > < pre > drawElements();
}
delete optionsForm;
}
< / pre >
< p > The form for setting options is provided by our custom < tt > OptionsForm< / tt >
covered in < a href = "tutorial2-09.html" > Setting Options< / a > . 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.
< p >
< pre > void ChartForm::optionsSetData()
{
SetDataForm *setDataForm = new SetDataForm( & m_elements, m_decimalPlaces, this );
if ( setDataForm-> < a href = "ntqdialog.html#exec" > exec< / a > () ) {
m_changed = TRUE;
drawElements();
}
delete setDataForm;
}
< / pre >
< p > The form for creating and editing chart data is provided by our custom
< tt > SetDataForm< / tt > covered in < a href = "tutorial2-08.html" > Taking Data< / a > .
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 > < p align = "right" >
< a href = "tutorial2-04.html" > « Mainly Easy< / a > |
< a href = "tutorial2.html" > Contents< / a > |
< a href = "tutorial2-06.html" > Canvas Control » < / 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 > TQt 3.3.8< / div >
< / table > < / div > < / address > < / body >
< / html >