1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
---|
2 | <!-- /home/espenr/tmp/qt-3.3.8-espenr-2499/qt-x11-free-3.3.8/doc/tutorial2.doc:349 -->
|
---|
3 | <html>
|
---|
4 | <head>
|
---|
5 | <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
---|
6 | <title>Presenting the GUI</title>
|
---|
7 | <style type="text/css"><!--
|
---|
8 | fn { margin-left: 1cm; text-indent: -1cm; }
|
---|
9 | a:link { color: #004faf; text-decoration: none }
|
---|
10 | a:visited { color: #672967; text-decoration: none }
|
---|
11 | body { background: #ffffff; color: black; }
|
---|
12 | --></style>
|
---|
13 | </head>
|
---|
14 | <body>
|
---|
15 |
|
---|
16 | <table border="0" cellpadding="0" cellspacing="0" width="100%">
|
---|
17 | <tr bgcolor="#E5E5E5">
|
---|
18 | <td valign=center>
|
---|
19 | <a href="index.html">
|
---|
20 | <font color="#004faf">Home</font></a>
|
---|
21 | | <a href="classes.html">
|
---|
22 | <font color="#004faf">All Classes</font></a>
|
---|
23 | | <a href="mainclasses.html">
|
---|
24 | <font color="#004faf">Main Classes</font></a>
|
---|
25 | | <a href="annotated.html">
|
---|
26 | <font color="#004faf">Annotated</font></a>
|
---|
27 | | <a href="groups.html">
|
---|
28 | <font color="#004faf">Grouped Classes</font></a>
|
---|
29 | | <a href="functions.html">
|
---|
30 | <font color="#004faf">Functions</font></a>
|
---|
31 | </td>
|
---|
32 | <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>
|
---|
33 |
|
---|
34 |
|
---|
35 | <p>
|
---|
36 | <p> <center><img src="chart-main2.png" alt="The chart application"></center>
|
---|
37 | <p> The <tt>chart</tt> application provides access to options via menus and
|
---|
38 | toolbar buttons arranged around a central widget, a CanvasView, in a
|
---|
39 | conventional document-centric style.
|
---|
40 | <p> (Extracts from <tt>chartform.h</tt>.)
|
---|
41 | <p>
|
---|
42 |
|
---|
43 | <pre> class ChartForm: public <a href="qmainwindow.html">QMainWindow</a>
|
---|
44 | {
|
---|
45 | <a href="metaobjects.html#Q_OBJECT">Q_OBJECT</a>
|
---|
46 | public:
|
---|
47 | enum { MAX_ELEMENTS = 100 };
|
---|
48 | enum { MAX_RECENTFILES = 9 }; // Must not exceed 9
|
---|
49 | enum ChartType { PIE, VERTICAL_BAR, HORIZONTAL_BAR };
|
---|
50 | enum AddValuesType { NO, YES, AS_PERCENTAGE };
|
---|
51 |
|
---|
52 | ChartForm( const <a href="qstring.html">QString</a>& filename );
|
---|
53 | ~ChartForm();
|
---|
54 |
|
---|
55 | int chartType() { return m_chartType; }
|
---|
56 | void setChanged( bool changed = TRUE ) { m_changed = changed; }
|
---|
57 | void drawElements();
|
---|
58 |
|
---|
59 | <a href="qpopupmenu.html">QPopupMenu</a> *optionsMenu; // Why public? See canvasview.cpp
|
---|
60 |
|
---|
61 | protected:
|
---|
62 | virtual void closeEvent( <a href="qcloseevent.html">QCloseEvent</a> * );
|
---|
63 |
|
---|
64 | private slots:
|
---|
65 | void fileNew();
|
---|
66 | void fileOpen();
|
---|
67 | void fileOpenRecent( int index );
|
---|
68 | void fileSave();
|
---|
69 | void fileSaveAs();
|
---|
70 | void fileSaveAsPixmap();
|
---|
71 | void filePrint();
|
---|
72 | void fileQuit();
|
---|
73 | void optionsSetData();
|
---|
74 | void updateChartType( <a href="qaction.html">QAction</a> *action );
|
---|
75 | void optionsSetFont();
|
---|
76 | void optionsSetOptions();
|
---|
77 | void helpHelp();
|
---|
78 | void helpAbout();
|
---|
79 | void helpAboutQt();
|
---|
80 | void saveOptions();
|
---|
81 |
|
---|
82 | private:
|
---|
83 | void init();
|
---|
84 | void load( const <a href="qstring.html">QString</a>& filename );
|
---|
85 | bool okToClear();
|
---|
86 | void drawPieChart( const double scales[], double total, int count );
|
---|
87 | void drawVerticalBarChart( const double scales[], double total, int count );
|
---|
88 | void drawHorizontalBarChart( const double scales[], double total, int count );
|
---|
89 |
|
---|
90 | <a href="qstring.html">QString</a> valueLabel( const <a href="qstring.html">QString</a>& label, double value, double total );
|
---|
91 | void updateRecentFiles( const <a href="qstring.html">QString</a>& filename );
|
---|
92 | void updateRecentFilesMenu();
|
---|
93 | void setChartType( ChartType chartType );
|
---|
94 |
|
---|
95 | <a href="qpopupmenu.html">QPopupMenu</a> *fileMenu;
|
---|
96 | <a href="qaction.html">QAction</a> *optionsPieChartAction;
|
---|
97 | <a href="qaction.html">QAction</a> *optionsHorizontalBarChartAction;
|
---|
98 | <a href="qaction.html">QAction</a> *optionsVerticalBarChartAction;
|
---|
99 | <a href="qstring.html">QString</a> m_filename;
|
---|
100 | <a href="qstringlist.html">QStringList</a> m_recentFiles;
|
---|
101 | <a href="qcanvas.html">QCanvas</a> *m_canvas;
|
---|
102 | CanvasView *m_canvasView;
|
---|
103 | bool m_changed;
|
---|
104 | ElementVector m_elements;
|
---|
105 | <a href="qprinter.html">QPrinter</a> *m_printer;
|
---|
106 | ChartType m_chartType;
|
---|
107 | AddValuesType m_addValues;
|
---|
108 | int m_decimalPlaces;
|
---|
109 | <a href="qfont.html">QFont</a> m_font;
|
---|
110 | };
|
---|
111 | </pre>
|
---|
112 | <p> We create a <tt>ChartForm</tt> subclass of <a href="qmainwindow.html">QMainWindow</a>. Our subclass uses
|
---|
113 | the <a href="metaobjects.html#Q_OBJECT">Q_OBJECT</a> macro to support Qt's <a href="signalsandslots.html">signals and slots</a> mechanism.
|
---|
114 | <p> The public interface is very small; the type of chart being displayed
|
---|
115 | can be retrieved, the chart can be marked 'changed' (so that the user
|
---|
116 | will be prompted to save on exit), and the chart can be asked to draw
|
---|
117 | itself (drawElements()). We've also made the options menu public
|
---|
118 | because we are also going to use this menu as the canvas view's
|
---|
119 | context menu.
|
---|
120 | <p> <center><table cellpadding="4" cellspacing="2" border="0">
|
---|
121 | <tr bgcolor="#f0f0f0">
|
---|
122 | <td valign="top">The <a href="qcanvas.html">QCanvas</a> class is used for drawing 2D vector graphics. The
|
---|
123 | <a href="qcanvasview.html">QCanvasView</a> class is used to present a view of a canvas in an
|
---|
124 | application's GUI. All our drawing operations take place on the
|
---|
125 | canvas; but events (e.g. mouse clicks) take place on the canvas view.
|
---|
126 | </table></center>
|
---|
127 | <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
|
---|
128 | functions and data members; we'll look at all these as we go through
|
---|
129 | the implementation.
|
---|
130 | <p> For the sake of convenience and compilation speed the chart form's
|
---|
131 | implementation is split over three files, <tt>chartform.cpp</tt> for the
|
---|
132 | 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.
|
---|
133 | <p> <h2> The Chart Form GUI
|
---|
134 | </h2>
|
---|
135 | <a name="1"></a><p> (Extracts from <tt>chartform.cpp</tt>.)
|
---|
136 | <p>
|
---|
137 |
|
---|
138 | <pre> #include "images/file_new.xpm"
|
---|
139 | #include "images/file_open.xpm"
|
---|
140 | </pre><pre> #include "images/options_piechart.xpm"
|
---|
141 | </pre>
|
---|
142 | <p> All the images used by <tt>chart</tt> have been created as <tt>.xpm</tt> files
|
---|
143 | which we've placed in the <tt>images</tt> subdirectory.
|
---|
144 | <p> <h2> The Constructor
|
---|
145 | </h2>
|
---|
146 | <a name="2"></a><p> <pre> ChartForm::ChartForm( const <a href="qstring.html">QString</a>& filename )
|
---|
147 | : <a href="qmainwindow.html">QMainWindow</a>( 0, 0, WDestructiveClose )
|
---|
148 | </pre><tt>...</tt>
|
---|
149 | <pre> <a href="qaction.html">QAction</a> *fileNewAction;
|
---|
150 | <a href="qaction.html">QAction</a> *fileOpenAction;
|
---|
151 | <a href="qaction.html">QAction</a> *fileSaveAction;
|
---|
152 | </pre>
|
---|
153 | <p> For each user action we declare a <a href="qaction.html">QAction</a> pointer. Some actions are
|
---|
154 | declared in the header file because they need to be referred to
|
---|
155 | outside of the constructor.
|
---|
156 | <p> <center><table cellpadding="4" cellspacing="2" border="0">
|
---|
157 | <tr bgcolor="#d0d0d0">
|
---|
158 | <td valign="top">Most user actions are suitable as both menu items and as toolbar
|
---|
159 | buttons. Qt allows us to create a single QAction which can be added to
|
---|
160 | both a menu and a toolbar. This approach ensures that menu items and
|
---|
161 | toolbar buttons stay in sync and saves duplicating code.
|
---|
162 | </table></center>
|
---|
163 | <p> <pre> fileNewAction = new <a href="qaction.html">QAction</a>(
|
---|
164 | "New Chart", QPixmap( file_new ),
|
---|
165 | "&New", CTRL+Key_N, this, "new" );
|
---|
166 | <a href="qobject.html#connect">connect</a>( fileNewAction, SIGNAL( <a href="qaction.html#activated">activated</a>() ), this, SLOT( fileNew() ) );
|
---|
167 | </pre>
|
---|
168 | <p> When we construct an action we give it a name, an optional icon, a
|
---|
169 | menu text, and an accelerator short-cut key (or 0 if no accelerator is
|
---|
170 | required). We also make it a child of the form (by passing <tt>this</tt>).
|
---|
171 | 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
|
---|
172 | action's slot, in the snippet shown above, to fileNew().
|
---|
173 | <p> The chart types are all mutually exclusive: you can have a pie chart
|
---|
174 | <em>or</em> a vertical bar chart <em>or</em> a horizontal bar chart. This means
|
---|
175 | that if the user selects the pie chart menu option, the pie chart
|
---|
176 | toolbar button must be automatically selected too, and the other chart
|
---|
177 | menu options and toolbar buttons must be automatically unselected.
|
---|
178 | This behaviour is achieved by creating a <a href="qactiongroup.html">QActionGroup</a> and placing the
|
---|
179 | chart type actions in the group.
|
---|
180 | <p> <pre> <a href="qactiongroup.html">QActionGroup</a> *chartGroup = new <a href="qactiongroup.html">QActionGroup</a>( this ); // Connected later
|
---|
181 | chartGroup-><a href="qactiongroup.html#setExclusive">setExclusive</a>( TRUE );
|
---|
182 | </pre>
|
---|
183 | <p> The action group becomes a child of the form (<tt>this</tt>) and the
|
---|
184 | exlusive behaviour is achieved by the setExclusive() call.
|
---|
185 | <p> <pre> optionsPieChartAction = new <a href="qaction.html">QAction</a>(
|
---|
186 | "Pie Chart", QPixmap( options_piechart ),
|
---|
187 | "&Pie Chart", CTRL+Key_I, chartGroup, "pie chart" );
|
---|
188 | optionsPieChartAction-><a href="qaction.html#setToggleAction">setToggleAction</a>( TRUE );
|
---|
189 | </pre>
|
---|
190 | <p> Each action in the group is created in the same way as other actions,
|
---|
191 | except that the action's parent is the group rather than the form.
|
---|
192 | Because our chart type actions have an on/off state we call
|
---|
193 | setToggleAction(TRUE) for each of them. Note that we do not connect
|
---|
194 | the actions; instead, later on, we will connect the group to a slot
|
---|
195 | that will cause the canvas to redraw.
|
---|
196 | <p> <center><table cellpadding="4" cellspacing="2" border="0">
|
---|
197 | <tr bgcolor="#f0f0f0">
|
---|
198 | <td valign="top">Why haven't we connected the group straight away? Later in the
|
---|
199 | constructor we will read the user's options, one of which is the chart
|
---|
200 | type. We will then set the chart type accordingly. But at that point
|
---|
201 | we still won't have created a canvas or have any data, so all we want
|
---|
202 | to do is toggle the canvas type toolbar buttons, but not actually draw
|
---|
203 | the (at this point non-existent) canvas. <em>After</em> we have set the
|
---|
204 | canvas type we will connect the group.
|
---|
205 | </table></center>
|
---|
206 | <p> Once we've created all our user actions we can create the toolbars and
|
---|
207 | menu options that will allow the user to invoke them.
|
---|
208 | <p> <pre> <a href="qtoolbar.html">QToolBar</a>* fileTools = new <a href="qtoolbar.html">QToolBar</a>( this, "file operations" );
|
---|
209 | fileTools-><a href="qtoolbar.html#setLabel">setLabel</a>( "File Operations" );
|
---|
210 | fileNewAction-><a href="qaction.html#addTo">addTo</a>( fileTools );
|
---|
211 | fileOpenAction-><a href="qaction.html#addTo">addTo</a>( fileTools );
|
---|
212 | fileSaveAction-><a href="qaction.html#addTo">addTo</a>( fileTools );
|
---|
213 | </pre><tt>...</tt>
|
---|
214 | <pre> fileMenu = new <a href="qpopupmenu.html">QPopupMenu</a>( this );
|
---|
215 | <a href="qmainwindow.html#menuBar">menuBar</a>()->insertItem( "&File", fileMenu );
|
---|
216 | fileNewAction-><a href="qaction.html#addTo">addTo</a>( fileMenu );
|
---|
217 | fileOpenAction-><a href="qaction.html#addTo">addTo</a>( fileMenu );
|
---|
218 | fileSaveAction-><a href="qaction.html#addTo">addTo</a>( fileMenu );
|
---|
219 | </pre>
|
---|
220 | <p> Toolbar actions and menu options are easily created from QActions.
|
---|
221 | <p> As a convenience to our users we will restore the last window position
|
---|
222 | and size and list their recently used files. This is achieved by
|
---|
223 | writing out their settings when the application is closed and reading
|
---|
224 | them back when we construct the form.
|
---|
225 | <p> <pre> <a href="qsettings.html">QSettings</a> settings;
|
---|
226 | settings.<a href="qsettings.html#insertSearchPath">insertSearchPath</a>( QSettings::Windows, WINDOWS_REGISTRY );
|
---|
227 | int windowWidth = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "WindowWidth", 460 );
|
---|
228 | int windowHeight = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "WindowHeight", 530 );
|
---|
229 | int windowX = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "WindowX", -1 );
|
---|
230 | int windowY = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "WindowY", -1 );
|
---|
231 | setChartType( ChartType(
|
---|
232 | settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "ChartType", int(PIE) ) ) );
|
---|
233 | </pre><pre> m_font = QFont( "Helvetica", 18, QFont::Bold );
|
---|
234 | m_font.fromString(
|
---|
235 | settings.<a href="qsettings.html#readEntry">readEntry</a>( APP_KEY + "Font", m_font.toString() ) );
|
---|
236 | for ( int i = 0; i < MAX_RECENTFILES; ++i ) {
|
---|
237 | <a href="qstring.html">QString</a> filename = settings.<a href="qsettings.html#readEntry">readEntry</a>( APP_KEY + "File" +
|
---|
238 | QString::<a href="qstring.html#number">number</a>( i + 1 ) );
|
---|
239 | if ( !filename.<a href="qstring.html#isEmpty">isEmpty</a>() )
|
---|
240 | m_recentFiles.push_back( filename );
|
---|
241 | }
|
---|
242 | if ( m_recentFiles.count() )
|
---|
243 | updateRecentFilesMenu();
|
---|
244 | </pre>
|
---|
245 | <p> The <a href="qsettings.html">QSettings</a> class handles user settings in a platform-independent
|
---|
246 | way. We simply read and write settings, leaving QSettings to handle
|
---|
247 | the platform dependencies. The insertSearchPath() call does nothing
|
---|
248 | except under Windows so does not have to be <tt>#ifdef</tt>ed.
|
---|
249 | <p> We use readNumEntry() calls to obtain the chart form's last size and
|
---|
250 | position, providing default values if this is the first time it has
|
---|
251 | been run. The chart type is retrieved as an integer and cast to a
|
---|
252 | ChartType enum value. We create a default label font and then read the
|
---|
253 | "Font" setting, using the default we have just created if necessary.
|
---|
254 | <p> Although QSettings can handle string lists we've chosen to store each
|
---|
255 | recently used file as a separate entry to make it easier to hand edit
|
---|
256 | the settings. We attempt to read each possible file entry ("File1" to
|
---|
257 | "File9"), and add each non-empty entry to the list of recently used
|
---|
258 | files. If there are one or more recently used files we update the File
|
---|
259 | menu by calling updateRecentFilesMenu(); (we'll review this later on).
|
---|
260 | <p> <pre> <a href="qobject.html#connect">connect</a>( chartGroup, SIGNAL( <a href="qactiongroup.html#selected">selected</a>(QAction*) ),
|
---|
261 | this, SLOT( updateChartType(QAction*) ) );
|
---|
262 | </pre>
|
---|
263 | <p> Now that we have set the chart type (when we read it in as a user
|
---|
264 | setting) it is safe to connect the chart group to our
|
---|
265 | updateChartType() slot.
|
---|
266 | <p> <pre> <a href="qwidget.html#resize">resize</a>( windowWidth, windowHeight );
|
---|
267 | if ( windowX != -1 || windowY != -1 )
|
---|
268 | <a href="qwidget.html#move">move</a>( windowX, windowY );
|
---|
269 | </pre>
|
---|
270 | <p> And now that we know the window size and position we can resize and
|
---|
271 | move the chart form's window accordingly.
|
---|
272 | <p> <pre> m_canvas = new <a href="qcanvas.html">QCanvas</a>( this );
|
---|
273 | m_canvas-><a href="qcanvas.html#resize">resize</a>( <a href="qwidget.html#width">width</a>(), height() );
|
---|
274 | m_canvasView = new CanvasView( m_canvas, &m_elements, this );
|
---|
275 | <a href="qmainwindow.html#setCentralWidget">setCentralWidget</a>( m_canvasView );
|
---|
276 | m_canvasView-><a href="qwidget.html#show">show</a>();
|
---|
277 | </pre>
|
---|
278 | <p> We create a new <a href="qcanvas.html">QCanvas</a> and set its size to that of the chart form
|
---|
279 | window's client area. We also create a <tt>CanvasView</tt> (our own subclass
|
---|
280 | of <a href="qcanvasview.html">QCanvasView</a>) to display the QCanvas. We make the canvas view the
|
---|
281 | chart form's main widget and show it.
|
---|
282 | <p> <pre> if ( !filename.<a href="qstring.html#isEmpty">isEmpty</a>() )
|
---|
283 | load( filename );
|
---|
284 | else {
|
---|
285 | init();
|
---|
286 | m_elements[0].set( 20, red, 14, "Red" );
|
---|
287 | m_elements[1].set( 70, cyan, 2, "Cyan", darkGreen );
|
---|
288 | m_elements[2].set( 35, blue, 11, "Blue" );
|
---|
289 | m_elements[3].set( 55, yellow, 1, "Yellow", darkBlue );
|
---|
290 | m_elements[4].set( 80, magenta, 1, "Magenta" );
|
---|
291 | drawElements();
|
---|
292 | }
|
---|
293 | </pre>
|
---|
294 | <p> If we have a file to load we load it; otherwise we initialise our
|
---|
295 | elements vector and draw a sample chart.
|
---|
296 | <p> <pre> <a href="qmainwindow.html#statusBar">statusBar</a>()->message( "Ready", 2000 );
|
---|
297 | </pre>
|
---|
298 | <p> It is <em>vital</em> that we call statusBar() in the constructor, since the
|
---|
299 | call ensures that a status bar is created for this main window.
|
---|
300 | <p> <h3> init()
|
---|
301 | </h3>
|
---|
302 | <a name="2-1"></a><p> <pre> void ChartForm::init()
|
---|
303 | {
|
---|
304 | <a href="qwidget.html#setCaption">setCaption</a>( "Chart" );
|
---|
305 | m_filename = <a href="qstring.html#QString-null">QString::null</a>;
|
---|
306 | m_changed = FALSE;
|
---|
307 |
|
---|
308 | m_elements[0] = Element( Element::INVALID, red );
|
---|
309 | m_elements[1] = Element( Element::INVALID, cyan );
|
---|
310 | m_elements[2] = Element( Element::INVALID, blue );
|
---|
311 | </pre><tt>...</tt>
|
---|
312 | <p> We use an init() function because we want to initialise the canvas and
|
---|
313 | the elements (in the <tt>m_elements</tt> <tt>ElementVector</tt>) when the form is
|
---|
314 | constructed, and also whenever the user loads an existing data set or
|
---|
315 | creates a new data set.
|
---|
316 | <p> We reset the caption and set the current filename to QString::null. We
|
---|
317 | also populate the elements vector with invalid elements. This isn't
|
---|
318 | necessary, but giving each element a different color is more
|
---|
319 | convenient for the user since when they enter values each one will
|
---|
320 | already have a unique color (which they can change of course).
|
---|
321 | <p> <h2> The File Handling Actions
|
---|
322 | </h2>
|
---|
323 | <a name="3"></a><p> <h3> okToClear()
|
---|
324 | </h3>
|
---|
325 | <a name="3-1"></a><p> <pre> bool ChartForm::okToClear()
|
---|
326 | {
|
---|
327 | if ( m_changed ) {
|
---|
328 | <a href="qstring.html">QString</a> msg;
|
---|
329 | if ( m_filename.isEmpty() )
|
---|
330 | msg = "Unnamed chart ";
|
---|
331 | else
|
---|
332 | msg = QString( "Chart '%1'\n" ).arg( m_filename );
|
---|
333 | msg += "has been changed.";
|
---|
334 |
|
---|
335 | int x = QMessageBox::<a href="qmessagebox.html#information">information</a>( this, "Chart -- Unsaved Changes",
|
---|
336 | msg, "&Save", "Cancel", "&Abandon",
|
---|
337 | 0, 1 );
|
---|
338 | switch( x ) {
|
---|
339 | case 0: // Save
|
---|
340 | fileSave();
|
---|
341 | break;
|
---|
342 | case 1: // Cancel
|
---|
343 | default:
|
---|
344 | return FALSE;
|
---|
345 | case 2: // Abandon
|
---|
346 | break;
|
---|
347 | }
|
---|
348 | }
|
---|
349 |
|
---|
350 | return TRUE;
|
---|
351 | }
|
---|
352 | </pre>
|
---|
353 | <p> The okToClear() function is used to prompt the user to save their
|
---|
354 | values if they have any unsaved data. It is used by several other
|
---|
355 | functions.
|
---|
356 | <p> <h3> fileNew()
|
---|
357 | </h3>
|
---|
358 | <a name="3-2"></a><p>
|
---|
359 |
|
---|
360 | <pre> void ChartForm::fileNew()
|
---|
361 | {
|
---|
362 | if ( okToClear() ) {
|
---|
363 | init();
|
---|
364 | drawElements();
|
---|
365 | }
|
---|
366 | }
|
---|
367 | </pre>
|
---|
368 | <p> When the user invokes the fileNew() action we call okToClear() to give
|
---|
369 | them the opportunity to save any unsaved data. If they either save or
|
---|
370 | abandon or have no unsaved data we re-initialise the elements vector
|
---|
371 | and draw the default chart.
|
---|
372 | <p> <center><table cellpadding="4" cellspacing="2" border="0">
|
---|
373 | <tr bgcolor="#d0d0d0">
|
---|
374 | <td valign="top">Should we also have invoked optionsSetData() to pop up the dialog
|
---|
375 | through which the user can create and edit values, colors etc? You
|
---|
376 | could try running the application as it is, and then try it having
|
---|
377 | added a call to optionsSetData() and see which you prefer.
|
---|
378 | </table></center>
|
---|
379 | <p> <h3> fileOpen()
|
---|
380 | </h3>
|
---|
381 | <a name="3-3"></a><p> <pre> void ChartForm::fileOpen()
|
---|
382 | {
|
---|
383 | if ( !okToClear() )
|
---|
384 | return;
|
---|
385 |
|
---|
386 | <a href="qstring.html">QString</a> filename = QFileDialog::<a href="qfiledialog.html#getOpenFileName">getOpenFileName</a>(
|
---|
387 | QString::null, "Charts (*.cht)", this,
|
---|
388 | "file open", "Chart -- File Open" );
|
---|
389 | <a name="x2567"></a> if ( !filename.<a href="qstring.html#isEmpty">isEmpty</a>() )
|
---|
390 | load( filename );
|
---|
391 | else
|
---|
392 | <a href="qmainwindow.html#statusBar">statusBar</a>()->message( "File Open abandoned", 2000 );
|
---|
393 | }
|
---|
394 | </pre>
|
---|
395 | <p> We check that it is okToClear(). If it is we use the static
|
---|
396 | <a href="qfiledialog.html#getOpenFileName">QFileDialog::getOpenFileName</a>() function to get the name of the file
|
---|
397 | the user wishes to load. If we get a filename we call load().
|
---|
398 | <p> <h3> fileSaveAs()
|
---|
399 | </h3>
|
---|
400 | <a name="3-4"></a><p> <pre> void ChartForm::fileSaveAs()
|
---|
401 | {
|
---|
402 | <a href="qstring.html">QString</a> filename = QFileDialog::<a href="qfiledialog.html#getSaveFileName">getSaveFileName</a>(
|
---|
403 | QString::null, "Charts (*.cht)", this,
|
---|
404 | "file save as", "Chart -- File Save As" );
|
---|
405 | if ( !filename.<a href="qstring.html#isEmpty">isEmpty</a>() ) {
|
---|
406 | int answer = 0;
|
---|
407 | <a name="x2563"></a> if ( QFile::<a href="qfile.html#exists">exists</a>( filename ) )
|
---|
408 | <a name="x2566"></a> answer = QMessageBox::<a href="qmessagebox.html#warning">warning</a>(
|
---|
409 | this, "Chart -- Overwrite File",
|
---|
410 | QString( "Overwrite\n\'%1\'?" ).
|
---|
411 | arg( filename ),
|
---|
412 | "&Yes", "&No", QString::null, 1, 1 );
|
---|
413 | if ( answer == 0 ) {
|
---|
414 | m_filename = filename;
|
---|
415 | updateRecentFiles( filename );
|
---|
416 | fileSave();
|
---|
417 | return;
|
---|
418 | }
|
---|
419 | }
|
---|
420 | <a href="qmainwindow.html#statusBar">statusBar</a>()->message( "Saving abandoned", 2000 );
|
---|
421 | }
|
---|
422 | </pre>
|
---|
423 | <p> This function calls the static <a href="qfiledialog.html#getSaveFileName">QFileDialog::getSaveFileName</a>() to get
|
---|
424 | the name of the file to save the data in. If the file exists we use a
|
---|
425 | <a href="qmessagebox.html#warning">QMessageBox::warning</a>() to notify the user and give them the option of
|
---|
426 | abandoning the save. If the file is to be saved we update the recently
|
---|
427 | opened files list and call fileSave() (covered in <a href="tutorial2-07.html">File Handling</a>) to perform the save.
|
---|
428 | <p> <h2> Managing a list of Recently Opened Files
|
---|
429 | </h2>
|
---|
430 | <a name="4"></a><p>
|
---|
431 |
|
---|
432 | <pre> <a href="qstringlist.html">QStringList</a> m_recentFiles;
|
---|
433 | </pre>
|
---|
434 | <p> We hold the list of recently opened files in a string list.
|
---|
435 | <p>
|
---|
436 |
|
---|
437 | <pre> void ChartForm::updateRecentFilesMenu()
|
---|
438 | {
|
---|
439 | for ( int i = 0; i < MAX_RECENTFILES; ++i ) {
|
---|
440 | if ( fileMenu-><a href="qmenudata.html#findItem">findItem</a>( i ) )
|
---|
441 | fileMenu-><a href="qmenudata.html#removeItem">removeItem</a>( i );
|
---|
442 | if ( i < int(m_recentFiles.count()) )
|
---|
443 | fileMenu-><a href="qmenudata.html#insertItem">insertItem</a>( QString( "&%1 %2" ).
|
---|
444 | arg( i + 1 ).arg( m_recentFiles[i] ),
|
---|
445 | this, SLOT( fileOpenRecent(int) ),
|
---|
446 | 0, i );
|
---|
447 | }
|
---|
448 | }
|
---|
449 | </pre>
|
---|
450 | <p> This function is called (usually via updateRecentFiles()) whenever the
|
---|
451 | user opens an existing file or saves a new file. For each file in the
|
---|
452 | string list we insert a new menu item. We prefix each filename with an
|
---|
453 | underlined number from <u>1</u> to <u>9</u> to support keyboard access
|
---|
454 | (e.g. <tt>Alt+F, 2</tt> to open the second file in the list). We give the
|
---|
455 | menu item an id which is the same as the index position of the item in
|
---|
456 | the string list, and connect each menu item to the fileOpenRecent()
|
---|
457 | slot. The old file menu items are deleted at the same time by going
|
---|
458 | through each possible recent file menu item id. This works because the
|
---|
459 | other file menu items had ids created by Qt (all of which are < 0);
|
---|
460 | whereas the menu items we're creating all have ids >= 0.
|
---|
461 | <p>
|
---|
462 |
|
---|
463 | <pre> void ChartForm::updateRecentFiles( const <a href="qstring.html">QString</a>& filename )
|
---|
464 | {
|
---|
465 | if ( m_recentFiles.find( filename ) != m_recentFiles.end() )
|
---|
466 | return;
|
---|
467 |
|
---|
468 | m_recentFiles.push_back( filename );
|
---|
469 | if ( m_recentFiles.count() > MAX_RECENTFILES )
|
---|
470 | m_recentFiles.pop_front();
|
---|
471 |
|
---|
472 | updateRecentFilesMenu();
|
---|
473 | }
|
---|
474 | </pre>
|
---|
475 | <p> This is called when the user opens an existing file or saves a new
|
---|
476 | file. If the file is already in the list it simply returns. Otherwise
|
---|
477 | the file is added to the end of the list and if the list is too large
|
---|
478 | (> 9 files) the first (oldest) is removed. updateRecentFilesMenu() is
|
---|
479 | then called to recreate the list of recently used files in the File
|
---|
480 | menu.
|
---|
481 | <p>
|
---|
482 |
|
---|
483 | <pre> void ChartForm::fileOpenRecent( int index )
|
---|
484 | {
|
---|
485 | if ( !okToClear() )
|
---|
486 | return;
|
---|
487 |
|
---|
488 | load( m_recentFiles[index] );
|
---|
489 | }
|
---|
490 | </pre>
|
---|
491 | <p> When the user selects a recently opened file the fileOpenRecent() slot
|
---|
492 | is called with the menu id of the file they have selected. Because we
|
---|
493 | made the file menu ids equal to the files' index positions in the
|
---|
494 | <tt>m_recentFiles</tt> list we can simply load the file indexed by the menu
|
---|
495 | item id.
|
---|
496 | <p> <h2> Quiting
|
---|
497 | </h2>
|
---|
498 | <a name="5"></a><p> <pre> void ChartForm::fileQuit()
|
---|
499 | {
|
---|
500 | if ( okToClear() ) {
|
---|
501 | saveOptions();
|
---|
502 | qApp-><a href="qapplication.html#exit">exit</a>( 0 );
|
---|
503 | }
|
---|
504 | }
|
---|
505 | </pre>
|
---|
506 | <p> When the user quits we give them the opportunity to save any unsaved
|
---|
507 | data (okToClear()) then save their options, e.g. window size and
|
---|
508 | position, chart type, etc., before terminating.
|
---|
509 | <p> <pre> void ChartForm::saveOptions()
|
---|
510 | {
|
---|
511 | <a href="qsettings.html">QSettings</a> settings;
|
---|
512 | settings.<a href="qsettings.html#insertSearchPath">insertSearchPath</a>( QSettings::Windows, WINDOWS_REGISTRY );
|
---|
513 | settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "WindowWidth", width() );
|
---|
514 | settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "WindowHeight", height() );
|
---|
515 | settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "WindowX", x() );
|
---|
516 | settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "WindowY", y() );
|
---|
517 | settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "ChartType", int(m_chartType) );
|
---|
518 | settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "AddValues", int(m_addValues) );
|
---|
519 | settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "Decimals", m_decimalPlaces );
|
---|
520 | settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "Font", m_font.toString() );
|
---|
521 | for ( int i = 0; i < int(m_recentFiles.count()); ++i )
|
---|
522 | settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "File" + QString::number( i + 1 ),
|
---|
523 | m_recentFiles[i] );
|
---|
524 | }
|
---|
525 | </pre>
|
---|
526 | <p> Saving the user's options using <a href="qsettings.html">QSettings</a> is straight-forward.
|
---|
527 | <p> <h2> Custom Dialogs
|
---|
528 | </h2>
|
---|
529 | <a name="6"></a><p> We want the user to be able to set some options manually and to create
|
---|
530 | and edit values, value colors, etc.
|
---|
531 | <p>
|
---|
532 |
|
---|
533 | <pre> void ChartForm::optionsSetOptions()
|
---|
534 | {
|
---|
535 | OptionsForm *optionsForm = new OptionsForm( this );
|
---|
536 | optionsForm->chartTypeComboBox->setCurrentItem( m_chartType );
|
---|
537 | optionsForm-><a href="qwidget.html#setFont">setFont</a>( m_font );
|
---|
538 | </pre><pre> if ( optionsForm-><a href="qdialog.html#exec">exec</a>() ) {
|
---|
539 | setChartType( ChartType(
|
---|
540 | optionsForm->chartTypeComboBox->currentItem()) );
|
---|
541 | m_font = optionsForm-><a href="qwidget.html#font">font</a>();
|
---|
542 | </pre><pre> drawElements();
|
---|
543 | }
|
---|
544 | delete optionsForm;
|
---|
545 | }
|
---|
546 | </pre>
|
---|
547 | <p> The form for setting options is provided by our custom <tt>OptionsForm</tt>
|
---|
548 | covered in <a href="tutorial2-09.html">Setting Options</a>. The
|
---|
549 | options form is a standard "dumb" dialog: we create an instance, set
|
---|
550 | all its GUI elements to the relevant settings, and if the user clicked
|
---|
551 | "OK" (exec() returns a true value) we read back settings from the GUI
|
---|
552 | elements.
|
---|
553 | <p>
|
---|
554 |
|
---|
555 | <pre> void ChartForm::optionsSetData()
|
---|
556 | {
|
---|
557 | SetDataForm *setDataForm = new SetDataForm( &m_elements, m_decimalPlaces, this );
|
---|
558 | if ( setDataForm-><a href="qdialog.html#exec">exec</a>() ) {
|
---|
559 | m_changed = TRUE;
|
---|
560 | drawElements();
|
---|
561 | }
|
---|
562 | delete setDataForm;
|
---|
563 | }
|
---|
564 | </pre>
|
---|
565 | <p> The form for creating and editing chart data is provided by our custom
|
---|
566 | <tt>SetDataForm</tt> covered in <a href="tutorial2-08.html">Taking Data</a>.
|
---|
567 | This form is a "smart" dialog. We pass in the data structure we want
|
---|
568 | to work on, and the dialog handles the presentation of the data
|
---|
569 | structure itself. If the user clicks "OK" the dialog will update the
|
---|
570 | data structure and exec() will return a true value. All we need to do
|
---|
571 | in optionsSetData() if the user changed the data is mark the chart as
|
---|
572 | changed and call drawElements() to redraw the chart with the new and
|
---|
573 | updated data.
|
---|
574 | <p> <p align="right">
|
---|
575 | <a href="tutorial2-04.html">« Mainly Easy</a> |
|
---|
576 | <a href="tutorial2.html">Contents</a> |
|
---|
577 | <a href="tutorial2-06.html">Canvas Control »</a>
|
---|
578 | </p>
|
---|
579 | <p>
|
---|
580 | <!-- eof -->
|
---|
581 | <p><address><hr><div align=center>
|
---|
582 | <table width=100% cellspacing=0 border=0><tr>
|
---|
583 | <td>Copyright © 2007
|
---|
584 | <a href="troll.html">Trolltech</a><td align=center><a href="trademarks.html">Trademarks</a>
|
---|
585 | <td align=right><div align=right>Qt 3.3.8</div>
|
---|
586 | </table></div></address></body>
|
---|
587 | </html>
|
---|