⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tutorial2-05.html

📁 QT参考文档
💻 HTML
📖 第 1 页 / 共 2 页
字号:
<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>实现图形用户界面</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">函数</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>实现图形用户界面</h1>


<p> 
<p> <center><img src="chart-main2.png" alt="The chart application"></center> 
<p> <tt>chart</tt>程序提供了通过排列在中央窗口部件周围的菜单和工具条来访问选项,和一个通常的文档在中央的风格的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>&amp; filename );
        ~ChartForm();

        int chartType() { return m_chartType; }
        void setChanged( bool changed = true ) { m_changed = changed; }
        void drawElements();

        <a href="qpopupmenu.html">QPopupMenu</a> *optionsMenu; // 为什么是公有的?请看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>&amp; 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>&amp; label, double value, double total );
        void updateRecentFiles( const <a href="qstring.html">QString</a>&amp; 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> 我们创建了一个<a href="qmainwindow.html">QMainWindow</a>的子类<tt>ChartForm</tt>。我们的子类使用了<a href="metaobjects.html#Q_OBJECT">Q_OBJECT</a>宏来支持Qt的<a href="signalsandslots.html">信号和槽</a>机制。
<p> 公有接口是很少的,被显示的图表类型能够被追溯,图表可以被标记为“changed”(这样用户在退出的时候会被提示保存),并且图表可以要求拖拽自己(drawElements())。我们已经把选项菜单设为公有,因为我们也会把这个菜单作为画布视图的关联菜单。
<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>类用来在一个应用程序的图形用户界面中实现一个画布的视图。我们所有的绘制操作都发生在画布上,但是事件(比如鼠标点击)却发生在画布视图中。
</table></center>
<p> 每一个动作都被一个私有槽实现,比如<tt>fileNew()</tt>、<tt>optionsSetData()</tt>等等。我们也需要相当多的私有函数和数据成员,当我们执行这些实现的时候,我们来看看这些。
<p> 为了方便和编译速度的原因,图表视窗的实现被分为三个文件,<tt>chartform.cpp</tt>实现图形用户界面,<tt>chartform_canvas.cpp</tt>实现画布处理和<tt>chartform_files.cpp</tt>实现文件处理。我们会依次评论每一个。
<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>中使用的所有图像是我们已经创建好并放在<tt>images</tt>子目录中的<tt>.xpm</tt>文件。
<p> <h2> 构造函数
</h2>
<a name="2"></a><p> <pre>    ChartForm::ChartForm( const <a href="qstring.html">QString</a>&amp; 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> 对于每一个用户动作我们声明了一个<a href="qaction.html">QAction</a>指针。一些动作在头文件中已经声明,因为它们需要在构造函数外被参考。
<p> <center><table cellpadding="4" cellspacing="2" border="0">
<tr bgcolor="#d0d0d0">
<td valign="top">大部分用户动作适用于菜单条目和工具条按钮。Qt允许用户创建一个单一的QAction而被添加到菜单和工具条中。这种方法保证了菜单条目和工具条按钮处于同步状态并且可以节省代码。
</table></center>
<p> <pre>        fileNewAction = new <a href="qaction.html">QAction</a>(
                "New Chart", QPixmap( file_new ),
                "&amp;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> 当我们构造一个动作时,我们给它一个名字、一个可选的图标、一个菜单文本和一个加速快捷键(或者0如果不需要加速键)。我们也可以使它成为视窗的子对象(通过<tt>this</tt>)。当用户点击一个工具条按钮或者点击一个菜单选项时,<tt>activated()</tt>信号会被发射。我们把这个信号和这个动作的槽连接起来,就是上面的程序代码中提到的fileNew()。
<p> 图表类型是互斥的:我们可以用一个饼图<em>或</em>一个竖直条形图<em>或</em>一个水平条形图。这也就是说如果用户选择了饼图菜单选项,饼图工具条按钮也必须被自动地选中,并且其它图表菜单选项和工具条按钮必须被自动地取消选择。这种行为是通过创建一个<a href="qactiongroup.html">QActionGroup</a>来实现的并且把这些图表类型动作放到这个组中。
<p> <pre>        <a href="qactiongroup.html">QActionGroup</a> *chartGroup = new <a href="qactiongroup.html">QActionGroup</a>( this ); // Connected later
        chartGroup-&gt;<a href="qactiongroup.html#setExclusive">setExclusive</a>( true );
</pre>
<p> 动作组成为了视窗(<tt>this</tt>)的子对象并且exlusive行为通过setExclusive()调用实现的。
<p> <pre>        optionsPieChartAction = new <a href="qaction.html">QAction</a>(
                "Pie Chart", QPixmap( options_piechart ),
                "&amp;Pie Chart", CTRL+Key_I, chartGroup, "pie chart" );
        optionsPieChartAction-&gt;<a href="qaction.html#setToggleAction">setToggleAction</a>( true );
</pre>
<p> 组中的每一个动作都以和其它动作一样的方式创建,除了动作的父对象是组而不是视窗。因为我们的图表类型动作由开/关状态,我们为它们中的每一个调用setToggleAction(TRUE)。注意我们没有连接动作,相反,稍后我们会我们会把这个组连接到一个可以使画布重画的槽。
<p> <center><table cellpadding="4" cellspacing="2" border="0">
<tr bgcolor="#f0f0f0">
<td valign="top">为什么我们不马上连接这个组呢?稍后在构造函数中我们将会读取用户选项,图表类型之一。我们将会直接设置图表类型。但那时我们还没有创建画布或者有任何数据,所以我们想做的一切就是切换画布类型工具条按钮,而不是真正地画(这时还不存在的)画布。在我们设置好画布类型<em>之后</em>,我们将会连接这个组。
</table></center>
<p> 一旦我们已经创建完所有的用户动作,我们就可以创建工具条和菜单选项来允许用户调用它们。
<p> <pre>        <a href="qtoolbar.html">QToolBar</a>* fileTools = new <a href="qtoolbar.html">QToolBar</a>( this, "file operations" );
        fileTools-&gt;<a href="qtoolbar.html#setLabel">setLabel</a>( "File Operations" );
        fileNewAction-&gt;<a href="qaction.html#addTo">addTo</a>( fileTools );
        fileOpenAction-&gt;<a href="qaction.html#addTo">addTo</a>( fileTools );
        fileSaveAction-&gt;<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>()-&gt;insertItem( "&amp;File", fileMenu );
        fileNewAction-&gt;<a href="qaction.html#addTo">addTo</a>( fileMenu );
        fileOpenAction-&gt;<a href="qaction.html#addTo">addTo</a>( fileMenu );
        fileSaveAction-&gt;<a href="qaction.html#addTo">addTo</a>( fileMenu );
</pre>
<p> 工具条动作和菜单选项可以很容易地由QAction生成。
<p> 作为一个对我们的用户提供的方便,我们将会重新载入上次窗口的位置和大小并列出最近使用的文件。这是通过在程序退出的时候写出这些设置,在我们构造视窗的时候再把它们都回来实现的。
<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 &lt; 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>类通过和平台无关的方式来处理用户设置。我们很简单地读写设置,把处理平台依赖性的问题留给QSettings来处理。insertSearchPath()调用没有做任何事,除非在Windows下被<tt>#ifdef</tt>过。
<p> 我们使用readNumEntry()调用来得到图表视窗上次的大小和位置,并且为它的第一次运行提供了默认值。图表类型是以一个整数重新获得并把它扔给CharType枚举值。我们创建默认标签字体,然后读取“Font”设置,如果需要的话我们使用刚才生成的默认字体。
<p> 尽管QSettings可以处理字符串列表,但是我们已经选择把最近使用的每一个文件作为单一的条目来存储,这样就可以更容易地处理和编辑这些设置。我们试着去读每一个可能的文件条目(从“File1”到“File9”),并把每一个非空条目添加到最近使用的文件的列表中。如果有一个或多个最近使用的文件,我们通过调用updateRecentFilesMenu()来更新File菜单,(我们将会在稍后再评论这个)。
<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> 现在我们已经设置图表类型(当我们把它作为一个用户设置读入的时候),把图表组和我们的updateChartType()槽连接起来是安全的。
<p> <pre>        <a href="qwidget.html#resize">resize</a>( windowWidth, windowHeight );
        <a href="qwidget.html#move">move</a>( windowX, windowY );
</pre>
<p> 并且现在我们已经知道窗口大小和位置,我们就可以根据这些重新定义大小并移动图表视窗窗口。
<p> <pre>        m_canvas = new <a href="qcanvas.html">QCanvas</a>( this );
        m_canvas-&gt;<a href="qcanvas.html#resize">resize</a>( <a href="qwidget.html#width">width</a>(), height() );
        m_canvasView = new CanvasView( m_canvas, &amp;m_elements, this );
        <a href="qmainwindow.html#setCentralWidget">setCentralWidget</a>( m_canvasView );
        m_canvasView-&gt;<a href="qwidget.html#show">show</a>();
</pre>
<p> 我们创建一个新的<a href="qcanvas.html">QCanvas</a>并且设置它的大小为图表视窗窗口的客户区域。我们也创建一个<tt>CanvasView</tt>(我们自己的<a href="qcanvasview.html">QCanvasView</a>的子类)来显示QCanvas。我们把这个画布视图作为图表视窗的主窗口部件并显示它。
<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> 如果我们有一个文件要载入,我们就载入它,否则我们就初始化我们的元素矢量并画一个示例图表。
<p> <pre>        <a href="qmainwindow.html#statusBar">statusBar</a>()-&gt;message( "Ready", 2000 );
</pre>
<p> 我们在构造函数中调用statusBar()是<em>非常重要的</em>,因为这个调用保证了我们能够在这个主窗口中创建一个状态条。
<p> <h3> init()
</h3>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -