?? tutorial2-05.html
字號(hào):
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<meta name="Translator" content="Cavendish">
<meta name="Qt zh_CN Documents Website" content="http://www.qiliang.net/qt">
<title>實(shí)現(xiàn)圖形用戶界面</title>
<style type="text/css"><!--
h3.fn,span.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; font-family: "Times New Roman" }
--></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">主頁</font></a>
| <a href="classes.html">
<font color="#004faf">所有的類</font></a>
| <a href="mainclasses.html">
<font color="#004faf">主要的類</font></a>
| <a href="annotated.html">
<font color="#004faf">注釋的類</font></a>
| <a href="groups.html">
<font color="#004faf">分組的類</font></a>
| <a href="functions.html">
<font color="#004faf">函數(shù)</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>實(shí)現(xiàn)圖形用戶界面</h1>
<p>
<p> <center><img src="chart-main2.png" alt="The chart application"></center>
<p> <tt>chart</tt>程序提供了通過排列在中央窗口部件周圍的菜單和工具條來訪問選項(xiàng),和一個(gè)通常的文檔在中央的風(fēng)格的CanvasView。
<p> (由<tt>chartform.h</tt>展開。)
<p>
<pre> class ChartForm: public <a href="qmainwindow.html">QMainWindow</a>
{
<a href="metaobjects.html#Q_OBJECT">Q_OBJECT</a>
public:
enum { MAX_ELEMENTS = 100 };
enum { MAX_RECENTFILES = 9 }; // 必須不超過9
enum ChartType { PIE, VERTICAL_BAR, HORIZONTAL_BAR };
enum AddValuesType { NO, YES, AS_PERCENTAGE };
ChartForm( const <a href="qstring.html">QString</a>& filename );
~ChartForm();
int chartType() { return m_chartType; }
void setChanged( bool changed = true ) { m_changed = changed; }
void drawElements();
<a href="qpopupmenu.html">QPopupMenu</a> *optionsMenu; // 為什么是公有的?請(qǐng)看canvasview.cpp。
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="qaction.html">QAction</a> *action );
void optionsSetFont();
void optionsSetOptions();
void helpHelp();
void helpAbout();
void helpAboutQt();
void saveOptions();
private:
void init();
void load( const <a href="qstring.html">QString</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="qstring.html">QString</a> valueLabel( const <a href="qstring.html">QString</a>& label, double value, double total );
void updateRecentFiles( const <a href="qstring.html">QString</a>& filename );
void updateRecentFilesMenu();
void setChartType( ChartType chartType );
<a href="qpopupmenu.html">QPopupMenu</a> *fileMenu;
<a href="qaction.html">QAction</a> *optionsPieChartAction;
<a href="qaction.html">QAction</a> *optionsHorizontalBarChartAction;
<a href="qaction.html">QAction</a> *optionsVerticalBarChartAction;
<a href="qstring.html">QString</a> m_filename;
<a href="qstringlist.html">QStringList</a> m_recentFiles;
<a href="qcanvas.html">QCanvas</a> *m_canvas;
CanvasView *m_canvasView;
bool m_changed;
ElementVector m_elements;
<a href="qprinter.html">QPrinter</a> *m_printer;
ChartType m_chartType;
AddValuesType m_addValues;
int m_decimalPlaces;
<a href="qfont.html">QFont</a> m_font;
};
</pre>
<p> 我們創(chuàng)建了一個(gè)<a href="qmainwindow.html">QMainWindow</a>的子類<tt>ChartForm</tt>。我們的子類使用了<a href="metaobjects.html#Q_OBJECT">Q_OBJECT</a>宏來支持Qt的<a href="signalsandslots.html">信號(hào)和槽</a>機(jī)制。
<p> 公有接口是很少的,被顯示的圖表類型能夠被追溯,圖表可以被標(biāo)記為“changed”(這樣用戶在退出的時(shí)候會(huì)被提示保存),并且圖表可以要求拖拽自己(drawElements())。我們已經(jīng)把選項(xiàng)菜單設(shè)為公有,因?yàn)槲覀円矔?huì)把這個(gè)菜單作為畫布視圖的關(guān)聯(lián)菜單。
<p> <center><table cellpadding="4" cellspacing="2" border="0">
<tr bgcolor="#f0f0f0">
<td valign="top"><a href="qcanvas.html">QCanvas</a>類用來繪制二維矢量圖。<a href="qcanvasview.html">QCanvasView</a>類用來在一個(gè)應(yīng)用程序的圖形用戶界面中實(shí)現(xiàn)一個(gè)畫布的視圖。我們所有的繪制操作都發(fā)生在畫布上,但是事件(比如鼠標(biāo)點(diǎn)擊)卻發(fā)生在畫布視圖中。
</table></center>
<p> 每一個(gè)動(dòng)作都被一個(gè)私有槽實(shí)現(xiàn),比如<tt>fileNew()</tt>、<tt>optionsSetData()</tt>等等。我們也需要相當(dāng)多的私有函數(shù)和數(shù)據(jù)成員,當(dāng)我們執(zhí)行這些實(shí)現(xiàn)的時(shí)候,我們來看看這些。
<p> 為了方便和編譯速度的原因,圖表視窗的實(shí)現(xiàn)被分為三個(gè)文件,<tt>chartform.cpp</tt>實(shí)現(xiàn)圖形用戶界面,<tt>chartform_canvas.cpp</tt>實(shí)現(xiàn)畫布處理和<tt>chartform_files.cpp</tt>實(shí)現(xiàn)文件處理。我們會(huì)依次評(píng)論每一個(gè)。
<p> <h2> 圖表視窗圖形用戶界面
</h2>
<a name="1"></a><p> (由<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> <tt>chart</tt>中使用的所有圖像是我們已經(jīng)創(chuàng)建好并放在<tt>images</tt>子目錄中的<tt>.xpm</tt>文件。
<p> <h2> 構(gòu)造函數(shù)
</h2>
<a name="2"></a><p> <pre> ChartForm::ChartForm( const <a href="qstring.html">QString</a>& filename )
: <a href="qmainwindow.html">QMainWindow</a>( 0, 0, WDestructiveClose )
</pre><tt>...</tt>
<pre> <a href="qaction.html">QAction</a> *fileNewAction;
<a href="qaction.html">QAction</a> *fileOpenAction;
<a href="qaction.html">QAction</a> *fileSaveAction;
</pre>
<p> 對(duì)于每一個(gè)用戶動(dòng)作我們聲明了一個(gè)<a href="qaction.html">QAction</a>指針。一些動(dòng)作在頭文件中已經(jīng)聲明,因?yàn)樗鼈冃枰跇?gòu)造函數(shù)外被參考。
<p> <center><table cellpadding="4" cellspacing="2" border="0">
<tr bgcolor="#d0d0d0">
<td valign="top">大部分用戶動(dòng)作適用于菜單條目和工具條按鈕。Qt允許用戶創(chuàng)建一個(gè)單一的QAction而被添加到菜單和工具條中。這種方法保證了菜單條目和工具條按鈕處于同步狀態(tài)并且可以節(jié)省代碼。
</table></center>
<p> <pre> fileNewAction = new <a href="qaction.html">QAction</a>(
"New Chart", QPixmap( file_new ),
"&New", CTRL+Key_N, this, "new" );
<a href="qobject.html#connect">connect</a>( fileNewAction, SIGNAL( <a href="qaction.html#activated">activated</a>() ), this, SLOT( fileNew() ) );
</pre>
<p> 當(dāng)我們構(gòu)造一個(gè)動(dòng)作時(shí),我們給它一個(gè)名字、一個(gè)可選的圖標(biāo)、一個(gè)菜單文本和一個(gè)加速快捷鍵(或者0如果不需要加速鍵)。我們也可以使它成為視窗的子對(duì)象(通過<tt>this</tt>)。當(dāng)用戶點(diǎn)擊一個(gè)工具條按鈕或者點(diǎn)擊一個(gè)菜單選項(xiàng)時(shí),<tt>activated()</tt>信號(hào)會(huì)被發(fā)射。我們把這個(gè)信號(hào)和這個(gè)動(dòng)作的槽連接起來,就是上面的程序代碼中提到的fileNew()。
<p> 圖表類型是互斥的:我們可以用一個(gè)餅圖<em>或</em>一個(gè)豎直條形圖<em>或</em>一個(gè)水平條形圖。這也就是說如果用戶選擇了餅圖菜單選項(xiàng),餅圖工具條按鈕也必須被自動(dòng)地選中,并且其它圖表菜單選項(xiàng)和工具條按鈕必須被自動(dòng)地取消選擇。這種行為是通過創(chuàng)建一個(gè)<a href="qactiongroup.html">QActionGroup</a>來實(shí)現(xiàn)的并且把這些圖表類型動(dòng)作放到這個(gè)組中。
<p> <pre> <a href="qactiongroup.html">QActionGroup</a> *chartGroup = new <a href="qactiongroup.html">QActionGroup</a>( this ); // Connected later
chartGroup-><a href="qactiongroup.html#setExclusive">setExclusive</a>( true );
</pre>
<p> 動(dòng)作組成為了視窗(<tt>this</tt>)的子對(duì)象并且exlusive行為通過setExclusive()調(diào)用實(shí)現(xiàn)的。
<p> <pre> optionsPieChartAction = new <a href="qaction.html">QAction</a>(
"Pie Chart", QPixmap( options_piechart ),
"&Pie Chart", CTRL+Key_I, chartGroup, "pie chart" );
optionsPieChartAction-><a href="qaction.html#setToggleAction">setToggleAction</a>( true );
</pre>
<p> 組中的每一個(gè)動(dòng)作都以和其它動(dòng)作一樣的方式創(chuàng)建,除了動(dòng)作的父對(duì)象是組而不是視窗。因?yàn)槲覀兊膱D表類型動(dòng)作由開/關(guān)狀態(tài),我們?yōu)樗鼈冎械拿恳粋€(gè)調(diào)用setToggleAction(TRUE)。注意我們沒有連接動(dòng)作,相反,稍后我們會(huì)我們會(huì)把這個(gè)組連接到一個(gè)可以使畫布重畫的槽。
<p> <center><table cellpadding="4" cellspacing="2" border="0">
<tr bgcolor="#f0f0f0">
<td valign="top">為什么我們不馬上連接這個(gè)組呢?稍后在構(gòu)造函數(shù)中我們將會(huì)讀取用戶選項(xiàng),圖表類型之一。我們將會(huì)直接設(shè)置圖表類型。但那時(shí)我們還沒有創(chuàng)建畫布或者有任何數(shù)據(jù),所以我們想做的一切就是切換畫布類型工具條按鈕,而不是真正地畫(這時(shí)還不存在的)畫布。在我們?cè)O(shè)置好畫布類型<em>之后</em>,我們將會(huì)連接這個(gè)組。
</table></center>
<p> 一旦我們已經(jīng)創(chuàng)建完所有的用戶動(dòng)作,我們就可以創(chuàng)建工具條和菜單選項(xiàng)來允許用戶調(diào)用它們。
<p> <pre> <a href="qtoolbar.html">QToolBar</a>* fileTools = new <a href="qtoolbar.html">QToolBar</a>( this, "file operations" );
fileTools-><a href="qtoolbar.html#setLabel">setLabel</a>( "File Operations" );
fileNewAction-><a href="qaction.html#addTo">addTo</a>( fileTools );
fileOpenAction-><a href="qaction.html#addTo">addTo</a>( fileTools );
fileSaveAction-><a href="qaction.html#addTo">addTo</a>( fileTools );
</pre><tt>...</tt>
<pre> fileMenu = new <a href="qpopupmenu.html">QPopupMenu</a>( this );
<a href="qmainwindow.html#menuBar">menuBar</a>()->insertItem( "&File", fileMenu );
fileNewAction-><a href="qaction.html#addTo">addTo</a>( fileMenu );
fileOpenAction-><a href="qaction.html#addTo">addTo</a>( fileMenu );
fileSaveAction-><a href="qaction.html#addTo">addTo</a>( fileMenu );
</pre>
<p> 工具條動(dòng)作和菜單選項(xiàng)可以很容易地由QAction生成。
<p> 作為一個(gè)對(duì)我們的用戶提供的方便,我們將會(huì)重新載入上次窗口的位置和大小并列出最近使用的文件。這是通過在程序退出的時(shí)候?qū)懗鲞@些設(shè)置,在我們構(gòu)造視窗的時(shí)候再把它們都回來實(shí)現(xiàn)的。
<p> <pre> <a href="qsettings.html">QSettings</a> settings;
settings.<a href="qsettings.html#insertSearchPath">insertSearchPath</a>( QSettings::Windows, WINDOWS_REGISTRY );
int windowWidth = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "WindowWidth", 460 );
int windowHeight = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "WindowHeight", 530 );
int windowX = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "WindowX", 0 );
int windowY = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "WindowY", 0 );
setChartType( ChartType(
settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "ChartType", int(PIE) ) ) );
</pre><pre> m_font = QFont( "Helvetica", 18, QFont::Bold );
m_font.fromString(
settings.<a href="qsettings.html#readEntry">readEntry</a>( APP_KEY + "Font", m_font.toString() ) );
for ( int i = 0; i < MAX_RECENTFILES; ++i ) {
<a href="qstring.html">QString</a> filename = settings.<a href="qsettings.html#readEntry">readEntry</a>( APP_KEY + "File" +
QString::<a href="qstring.html#number">number</a>( i + 1 ) );
if ( !filename.<a href="qstring.html#isEmpty">isEmpty</a>() )
m_recentFiles.push_back( filename );
}
if ( m_recentFiles.count() )
updateRecentFilesMenu();
</pre>
<p> <a href="qsettings.html">QSettings</a>類通過和平臺(tái)無關(guān)的方式來處理用戶設(shè)置。我們很簡(jiǎn)單地讀寫設(shè)置,把處理平臺(tái)依賴性的問題留給QSettings來處理。insertSearchPath()調(diào)用沒有做任何事,除非在Windows下被<tt>#ifdef</tt>過。
<p> 我們使用readNumEntry()調(diào)用來得到圖表視窗上次的大小和位置,并且為它的第一次運(yùn)行提供了默認(rèn)值。圖表類型是以一個(gè)整數(shù)重新獲得并把它扔給CharType枚舉值。我們創(chuàng)建默認(rèn)標(biāo)簽字體,然后讀取“Font”設(shè)置,如果需要的話我們使用剛才生成的默認(rèn)字體。
<p> 盡管QSettings可以處理字符串列表,但是我們已經(jīng)選擇把最近使用的每一個(gè)文件作為單一的條目來存儲(chǔ),這樣就可以更容易地處理和編輯這些設(shè)置。我們?cè)囍プx每一個(gè)可能的文件條目(從“File1”到“File9”),并把每一個(gè)非空條目添加到最近使用的文件的列表中。如果有一個(gè)或多個(gè)最近使用的文件,我們通過調(diào)用updateRecentFilesMenu()來更新File菜單,(我們將會(huì)在稍后再評(píng)論這個(gè))。
<p> <pre> <a href="qobject.html#connect">connect</a>( chartGroup, SIGNAL( <a href="qactiongroup.html#selected">selected</a>(QAction*) ),
this, SLOT( updateChartType(QAction*) ) );
</pre>
<p> 現(xiàn)在我們已經(jīng)設(shè)置圖表類型(當(dāng)我們把它作為一個(gè)用戶設(shè)置讀入的時(shí)候),把圖表組和我們的updateChartType()槽連接起來是安全的。
<p> <pre> <a href="qwidget.html#resize">resize</a>( windowWidth, windowHeight );
<a href="qwidget.html#move">move</a>( windowX, windowY );
</pre>
<p> 并且現(xiàn)在我們已經(jīng)知道窗口大小和位置,我們就可以根據(jù)這些重新定義大小并移動(dòng)圖表視窗窗口。
<p> <pre> m_canvas = new <a href="qcanvas.html">QCanvas</a>( this );
m_canvas-><a href="qcanvas.html#resize">resize</a>( <a href="qwidget.html#width">width</a>(), height() );
m_canvasView = new CanvasView( m_canvas, &m_elements, this );
<a href="qmainwindow.html#setCentralWidget">setCentralWidget</a>( m_canvasView );
m_canvasView-><a href="qwidget.html#show">show</a>();
</pre>
<p> 我們創(chuàng)建一個(gè)新的<a href="qcanvas.html">QCanvas</a>并且設(shè)置它的大小為圖表視窗窗口的客戶區(qū)域。我們也創(chuàng)建一個(gè)<tt>CanvasView</tt>(我們自己的<a href="qcanvasview.html">QCanvasView</a>的子類)來顯示QCanvas。我們把這個(gè)畫布視圖作為圖表視窗的主窗口部件并顯示它。
<p> <pre> if ( !filename.<a href="qstring.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> 如果我們有一個(gè)文件要載入,我們就載入它,否則我們就初始化我們的元素矢量并畫一個(gè)示例圖表。
<p> <pre> <a href="qmainwindow.html#statusBar">statusBar</a>()->message( "Ready", 2000 );
</pre>
<p> 我們?cè)跇?gòu)造函數(shù)中調(diào)用statusBar()是<em>非常重要的</em>,因?yàn)檫@個(gè)調(diào)用保證了我們能夠在這個(gè)主窗口中創(chuàng)建一個(gè)狀態(tài)條。
<p> <h3> init()
</h3>
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -