QT中QMainWindow、QWidget、QDialog的区别

QT中QMainWindow、QWidget、QDialog的区别

  • QT中QMainWindowQWidgetQDialog的区别
    • QMainWindow
    • QWidget
    • QDialog

QMainWindow

详细描述

QMainWindow类提供一个有菜单条、锚接窗口(例如工具条)和一个状态条的主应用程序窗口。

主窗口通常用在提供一个大的中央窗口部件(例如文本编辑或者绘制画布)以及周围菜单、工具条和一个状态条。QMainWindow常常被继承,因为这使得封装中央部件、菜单和工具条以及窗口状态变得更容易。继承使创建当用户点击菜单项或者工具条按钮时被调用的槽成为可能。你也可以使用Qt设计器来创建主窗口。我们将简要地回顾一下有关添加菜单项和工具条按钮,然后描述QMainWindow自己的便捷。

   QMainWindow *mw = new QMainWindow;
    QTextEdit *edit = new QTextEdit( mw, "editor" );
    edit->setFocus();
    mw->setCaption( "Main Window" );
    mw->setCentralWidget( edit );
    mw->show();

QMainWindow可以像上面那样显示地来被创建。中央窗口部件是通过setCentralWidget()设置地。弹出菜单可以被添加到默认工具条,窗口部件可以被添加到状态条,工具条和锚接窗口可以被添加到任何一个锚接区域。


        ApplicationWindow * mw = new ApplicationWindow();
        mw->setCaption( "Qt Example - Application" );
        mw->show();

上面代码中的ApplicationWindow是我们自己写的QMainWindow的子类,这是一个使用QMainWindow的常用方法。(源代码取自application/main.cpp、application/application.cpp、action/main.cpp和action/application.cpp。)

在继承的时候,我们在子类的构造函数中添加菜单项和工具条。如果我们已经直接创建了一个QMainWindow实例,我们可以很容易地通过传递QMainWindow实例代替作为父对象的this指针来添加菜单项和工具条。

        QPopupMenu * help = new QPopupMenu( this );
        menuBar()->insertItem( "&Help", help );
        help->insertItem( "&About", this, SLOT(about()), Key_F1 );
 这里写代码片们添加了有一个菜单项的新菜单。这个菜单已经被插入QMainWindow默认提供的并且可以通过menuBar()函数访问的菜单条。当这个菜单项被点击时,这个槽被调用。
QToolBar * fileTools = new QToolBar( this, "file operations" );
        fileTools->setLabel( "File Operations" );
        QToolButton * fileOpen
            = new QToolButton( openIcon, "Open File", QString::null,
                               this, SLOT(choose()), fileTools, "open file" );

这部分提取显示的是有一个工具条按钮的工具条的创建。QMainWindow为工具条提供了四个锚接区域。当一个工具条被作为QMainWindow(或者继承类)实例 的子对象被创建时,它将会被放置到一个锚接区域中(默认是Top锚接区域)。当这个工具条按钮被点击时,这个槽被调用。任何锚接窗口可以使用addDockWindow(),或者通过把QMainWindow作为父对象来创建的方法来被添加到一个锚接区域中。

e = new QTextEdit( this, "editor" );
        e->setFocus();
        setCentralWidget( e );
        statusBar()->message( "Ready", 2000 );

创建完菜单和工具条,我们创建一个大的中央窗口部件的实例,给它焦点并且把它设置为主窗口的中央窗口部件。在这个实例中,我们也已经通过statusBar()函数设置好了状态条,显示初始信息两秒。注意你可以添加其它的窗口部件到状态条重,例如标签,来显示更多的状态信息。详细情况请参考QStatusBar文档,特别是addWidget()函数。

通常我们想让一个工具条按钮和一个菜单项同步。例如,如果用户点击“加粗”工具条按钮,我们希望“加粗”菜单项被选中。这种同步可以通过创建操作并且把它们添加到工具条和菜单上来自动实现。

QAction * fileOpenAction;
        fileOpenAction = new QAction( "Open File", QPixmap( fileopen ), "&Open",
                                      CTRL+Key_O, this, "open" );
        connect( fileOpenAction, SIGNAL( activated() ) , this, SLOT( choose() ) );

这里我们创建了一个有图标的操作,这个图标要用在这个操作所被添加到的菜单和工具条中。我们也给定这个操作一个菜单名称“&Open”和一个键盘快捷键。我们已经建立的这个连接在用户点击这个菜单项或者这个工具条按钮时将会被使用。

QPopupMenu * file = new QPopupMenu( this );
        menuBar()->insertItem( "&File", file );
        fileOpenAction->addTo( file );

上面这部分提取显示一个弹出菜单的创建。我们把这个菜单添加到QMainWindow的菜单条中并且添加我们的操作。

        QToolBar * fileTools = new QToolBar( this, "file operations" );
        fileTools->setLabel( "File Operations" );
        fileOpenAction->addTo( fileTools );

这里我们创建一个作为QMainWindow的子对象的工具条并且把我们的操作添加到这个工具条中。

现在我们来探险QMainWindow提供的功能。

主窗口将会注意锚接区域和中央窗口部件的几何形状,但是中央窗口部件的其它所有方面都留给你了。如果你指定QMainWindow作为父对象来创建菜单条或者状态条,QMainWindow会自动检测它们,或者你可以使用提供的menuBar()和statusBar()函数。如果一个窗口部件也没有,menuBar()和statusBar()函数创建一个合适的窗口部件,并且更新窗口的布局来得到空间。

QMainWindow提供了一个连接到状态条的QToolTipGroup。toolTipGroup()函数提供了对默认QToolTipGroup的访问。它不能设置一个不同的工具提示组。

新的锚接窗口和工具条可以通过使用addDockWindow()来被添加到QMainWindow中。锚接窗口可以使用moveDockWindow()来移动并且使用removeDockWindow()来移除。QMainWindow允许默认锚接窗口(工具条)锚接在所有它的锚接区域中(上、下、左、右)。你可以使用setDockEnabled()为锚接窗口而使锚接区域生效/失效。当添加或者移动锚接窗口时,你可以指定它们的“边缘”(锚接区域)。当前可用的边缘有:Top、Left、Right、Bottom、Minimized (有效地一个“隐藏”锚接区域)和TornOff(浮动)。关于这些区域的解释请参考Qt::Dock。注意*ToolBar函数被考虑向后兼容,所有的新代码都应该使用*DockWindow函数。QToolBar是QDockWindow的子类,所以所有对于锚接窗口能工作地函数对于工具条也一样可以工作。如果用户通过点击锚接窗口地窗口句柄来最小化一个锚接窗口,那么锚接窗口将会被移到Minimized锚接区域。如果用户点击关闭按钮,那么锚接窗口被隐藏并且只能再次通过使用锚接窗口菜单来再次被显示。

一些函数会全局地改变QMainWindow的外观:

QDockWindow::setHorizontalStretchable()和QDockWindow::setVerticalStretchable()被用于指定锚接窗口和工具条是可延伸的。
setUsesBigPixmaps()被用于设置工具按钮是应该用小像素映射还是大像素映射(更多信息请参考QIconSet)。
setUsesTextLabel()被用于设置工具按钮是否应该在显示像素映射之外还显示文本标签(更多信息请参考QToolButton)。
用户可以把锚接窗口拖入任何生效的锚接区域中。锚接窗口也可以在一个锚接区域中被拖动,例如重新排列一些工具条的顺序。锚接窗口也可以被拖动出任何锚接区域(取消锚接或者“浮动”)。你可以使用setDockWindowsMovable()把锚接窗口设置为生效的(默认)和失效的。如果用户点击浮动锚接窗口的关闭按钮,然后锚接窗口将会消失。为了重新得到锚接窗口,用户必须右键点击一个锚接区域,弹出一个锚接窗口菜单,然后点击我们要恢复的锚接窗口的名字。可视的锚接窗口在菜单中它们的名字之前有一个勾。锚接窗口菜单会根据createDockWindowMenu()的需要自动被创建。因为它可能不总是适合把一个锚接窗口显示到这个菜单中,setAppropriate()函数被用来告知主窗口这个锚接窗口菜单是否应该包含一个特定的锚接窗口。双击锚接窗口句柄(通常在锚接窗口的左侧)会使这个锚接窗口取消锚接(浮动)。双击浮动锚接窗口的标题栏将会锚接这个浮动的锚接窗口。

Minimized边缘是一个隐藏的锚接区域。如果这个锚接区域是生效的,用户可以隐藏(最小化)锚接窗口或者通过点击锚接窗口句柄显示(恢复)一个最小化的锚接窗口。如果用户在一个句柄上方悬浮鼠标光标,锚接窗口的标题会被作为工具提示显示(请参考QDockWindow::caption()或QToolBar::label()),所以如果你使Minimized锚接区域生效,最好为每一个锚接窗口指定一个有意义的标题或者标签。为了能够程序化地最小化一个锚接窗口,请使用一个带有Minimized边缘的moveDockWindow()。

锚接窗口默认是被透明地移动,也就是在拖动期间,一个边框矩形会在屏幕上被绘制出来呈现锚接窗口在移动时的位置。如果你想让锚接窗口在移动时被正常显示,请使用setOpaqueMoving()。

锚接窗口的定位,也就是它的锚接区域和在锚接区域中的位置,可以通过调用getLocation()来被决定。可移动的锚接窗口可以通过lineUpDockWindows()被排列成一行来使浪费的空间最小化。锚接区域的指针可以由topDock()、leftDock()、rightDock()和bottomDock()提供。如果isCustomizable()返回真(它默认返回假),一个自定义的菜单项会被添加入弹出的锚接窗口菜单。如果你想提供这个额外的菜单项,重新实现isCustomizable()和customize(),例如,允许用户改变和主窗口以及它的工具条和锚接窗口相关的设置。

主窗口的菜单条默认是固定的(在上面)。如果你想有一个可移动的菜单条,创建一个QMenuBar作为一个在它自己可移动的锚接窗口中可伸展的窗口部件并且限制这个锚接窗口只在Top或Bottom锚接区域存在:

QToolBar *tb = new QToolBar( this );
    addDockWindow( tb, tr( "Menubar" ), Top, FALSE );
    QMenuBar *mb = new QMenuBar( tb );
    mb->setFrameStyle( QFrame::NoFrame );
    tb->setStretchableWidget( mb );
    setDockEnabled( tb, Left, FALSE );
    setDockEnabled( tb, Right, FALSE );

有多个锚接窗口的应用程序可以选择为了以后(例如,在下个对话中)恢复当前的锚接窗口布局来把它们保存起来。你可以通过使用QMainWindow的流操作符来做到这点。

为了保存所有锚接窗口的布局和位置,请这样做:

  QFile f( filename );
  if ( f.open( IO_WriteOnly ) ) {
      QTextStream ts( &f );
      ts << *mainWindow;
      f.close();
  }

为了恢复锚接窗口的位置和大小(通常是当这个应用程序再次开始),请这样做:

QFile f( filename );
  if ( f.open( IO_ReadOnly ) ) {
      QTextStream ts( &f );
      ts >> *mainWindow;
      f.close();
  }

QSettings类可以和这些流操作符联合使用来存放这个应用程序的设置。

QMainWindow对锚接窗口和工具条的管理很明显地是由QDockArea在后面完成的。

对于多文档界面(MDI),使用QWorkspace作为中央窗口部件。

把锚接窗口(例如工具条)添加到QMainWindow的锚接区域是很简单的。如果被提供的锚接区域对于你的应用程序是不够的,我们建议你创建一个QWidget的子类并且把你自己的锚接区域(请参考QDockArea)添加到这个子类中,因为QMainWindow只提供了由它提供的标准锚接区域的特定功能。

QWidget:

详细描述

QWidget类是所有用户界面对象的基类。
窗口部件是用户界面的一个原子:它从窗口系统接收鼠标、键盘和其它事件,并且在屏幕上绘制自己的表现。每一个窗口部件都是矩形,并且它们按Z轴顺序排列的。一个窗口部件可以被它的父窗口部件或者它前面的窗口部件盖住一部分。

QDialog是最普通的顶级窗口。不被嵌入到一个父窗口部件的窗口部件被叫做顶级窗口部件。通常情况下,顶级窗口部件是有框架和标题栏的窗口(尽管如果使用了一定的窗口部件标记,创建顶级窗口部件时也可能没有这些装饰。)在Qt中,QMainWindow和和不同的QDialog的子类是最普通的顶级窗口。

一个没有父窗口部件的窗口部件一直是顶级窗口部件。

非顶级窗口部件是子窗口部件。它们是它们的父窗口部件中的子窗口。你通常不能在视觉角度从它们的父窗口部件中辨别一个子窗口部件。在Qt中的绝大多数其它窗口部件仅仅作为子窗口部件才是有用的。(当然把一个按钮作为或者叫做顶级窗口部件也是可能的,但绝大多数人喜欢把他们的按钮放到其它按钮当中,比如QDialog。)

QWidget有很多成员函数,但是它们中的一些有少量的直接功能:例如,QWidget有一个字体属性,但是它自己从来不用。有很多继承它的子类提供了实际的功能,比如QPushButton、QListBox和QTabDialog等等。
每一个窗口部件构造函数接受两个或三个标准参数:

QWidget *parent = 0是新窗口部件的父窗口部件。如果为0(默认),新的窗口部件将是一个顶级窗口部件。如果不是,它将会使parent的一个孩子,并且被parent的几何形状所强迫(除非你指定WType_TopLevel作为窗口部件标记)。
const char *name = 0是新窗口部件的窗口部件名称。你可以使用name()来访问它。窗口部件名称很少被程序员用到,但是对于图形用户界面构造程序,比如Qt设计器,是相当重要的(你可以在Qt设计器中命名一个窗口部件,并且在你的代码中使用这个名字来连接它)。dumpObjectTree()调试函数也使用它。
WFlags f = 0(在可用的情况下)设置窗口部件标记,默认设置对于几乎所有窗口部件都是适用的,但是,举例来说,一个没有窗口系统框架的顶级窗口部件,你必须使用特定的标记。
tictac/tictac.cpp实例程序是一个简单窗口部件的好实例。它包含了一些的事件处理器(就像所有窗口部件所必须的那样)、一些被指定给它的定制处理(就像所有有用的窗口部件做的那样)并且还有一些孩子和联机。它所做的每一件事都响应对应的一个事件:这是到目前为止设计图形用户界面的最普通的方式。

你自己将需要为你的窗口部件提供内容,但是这里是一些简要的运行事件,从最普通的开始:

paintEvent() - 只要窗口部件需要被重绘就被调用。每个要显示输出的窗口部件必须实现它并且不在paintEvent()之外在屏幕上绘制是明智的。
resizeEvent() - 当窗口部件被重新定义大小时被调用。
mousePressEvent() - 当鼠标键被按下时被调用。有六个鼠标相关事件,但是鼠标按下和鼠标释放事件是到目前为止最重要的。当鼠标在窗口部件内或者当它使用grabMouse()来捕获鼠标时,它接收鼠标按下事件。
mouseReleaseEvent() - 当鼠标键被释放时被调用。当窗口部件已经接收相应的鼠标按下事件时,它接收鼠标释放事件。这也就是说如果用户在你的窗口部件内按下鼠标,然后拖着鼠标到其它某个地方,然后释放,你的窗口部件接收这个释放事件。这里有一个例外:如果出现在弹出菜单中,当鼠标键被按下时,这个弹出菜单立即会偷掉这个鼠标事件。
mouseDoubleClickEvent() - 和它看起来也许不太一样。如果用户双击,窗口部件接收一个鼠标按下事件(如果他们没有拿牢鼠标,也许会出现一个或两个鼠标移动事件)、一个鼠标释放事件并且最终是这个事件。直到你看到第二次点击是否到来之前,不能从一个双击中辨别一个点击。(这是为什么绝大多数图形用户界面图书建议双击是单击的一个扩展,而不是一个不同行为的触发的一个原因。)
如果你的窗口部件仅仅包含子窗口部件,你也许不需要实现任何一个事件处理器。如果你想检测在子窗口部件中的鼠标点击,请在父窗口部件的mousePressEvent()中调用子窗口部件的hasMouse()函数。

接收键盘的窗口部件需要重新实现一些更多的事件处理器:

keyPressEvent() - 只要键被按下和当键已经被按下足够长的时间可以自动重复了就被调用。注意如果Tab和Shift+Tab键被用在焦点变换机制中,它们仅仅被传递给窗口部件。为了强迫那些键被你的窗口部件处理,你必须重新实现QWidget::event()。
focusInEvent() - 当窗口部件获得键盘焦点(假设你已经调用setFocusPolicy())时被调用。写得好的窗口部件意味着它们能按照一种清晰但谨慎的方式来获得键盘焦点。
focusOutEvent() - 当窗口部件失去键盘焦点时被调用。
一些窗口部件也许需要实现一些不太普通的事件处理器:

mouseMoveEvent() - 只要当鼠标键被按下时鼠标移动就会被调用。举例来说,对于拖动,这个很有用。如果你调用setMouseTracking(TRUE),尽管没有鼠标键被按下,你也会获得鼠标移动事件。(注意这个使用鼠标跟踪的应用程序在低下的X连接下不是很有用。)(也可以参考拖放信息。)
keyReleaseEvent() - 只要键被释放和当如果这个键是自动重复的并且被按下一段时间时就被调用。在这种情况下窗口部件接收一个键释放事件并且对于每一个重复立即有一个键按下事件。注意如果Tab和Shift+Tab键被用在焦点变换机制中,它们仅仅被传递给窗口部件。为了强迫那些键被你的窗口部件处理,你必须重新实现QWidget::event()。
wheelEvent() – 当窗口部件拥有焦点时,只要用户转动鼠标滚轮就被调用。
enterEvent() - 当鼠标进入这个窗口部件屏幕空间时被调用。(这不包括被这个窗口部件的子窗口部件所拥有的屏幕空间。)
leaveEvent() - 当鼠标离开这个窗口部件的屏幕空间时被调用。
moveEvent() - 当窗口部件相对于它的父窗口部件已经被移动时被调用。
closeEvent() - 当用户关闭窗口部件时(或这当close()被调用时)被调用。
这里还有一些不太明显的事件。它们在qevent.h中被列出并且你需要重新实现event()来处理它们。event()的默认实现处理Tab和Shift+Tab(移动键盘焦点)并且其它绝大多数事件给上面提到的一个或更多的特定处理器。

当实现一个窗口部件时,还有一些更多的事情要考虑。

在构造函数中,在你可能收到一个事件的任何机会之前,请确认尽早地设置你的成员变量。
重新实现sizeHint()在绝大多数情况下都是很有用的并且使用setSizePolicy(),来设置正确的大小策略,这样你的同事可以更容易地设置布局管理器。一个大小策略可以让你为布局管理器提供好的默认情况,这样其它窗口部件可以很容易地包含和管理你的窗口部件。sizeHint()为这个窗口部件说明一个“好的”大小。
如果你的窗口部件是一个顶级窗口部件,setCaption()和setIcon()分别设置标题栏和图标。
也可以参考QEvent、QPainter、QGridLayout、QBoxLayout和抽象窗口部件类。

QDialog

详细描述

QDialog类是对话框窗口的基类。
对话框窗口是主要用于短期任务以及和用户进行简要通讯的顶级窗口。QDialog可以是模式的也可以是非模式的。QDialog支持扩展性并且可以提供返回值。它们可以有默认按钮。QDialog也可以有一个QSizeGrip在它的右下角,使用setSizeGripEnabled()。

注意QDialog使用父窗口部件的方法和Qt中其它类稍微不同。对话框总是顶级窗口部件,但是如果它有一个父对象,它的默认位置就是父对象的中间。它也将和父对象共享工具条条目。

这里有三种有用的对话框:

模式对话框就是阻塞同一应用程序中其它可视窗口的输入的对话框:用户必须完成这个对话框中的交互操作并且关闭了它之后才能访问应用程序中的其它任何窗口。模式对话框有它们自己的本地事件循环。用来让用户选择一个文件或者用来设置应用程序参数的对话框通常是模式的。调用exec()来显示模式对话框。当用户关闭这个对话框,exec()将提供一个可用的返回值并且这时流程控制继续从调用exec()的地方进行。通常,我们连接默认按钮,例如“OK”到accept()槽并且把“Cancel”连接到reject()槽,来使对话框关闭并且返回适当的值。另外我们也可以连接done()槽,传递给它Accepted或Rejected。
非模式对话框是和同一个程序中其它窗口操作无关的对话框。在字处理软件中查找和替换对话框通常是非模式的来允许同时与应用程序主窗口和对话框进行交互。调用show()来显示非模式对话框。show()立即返回,这样调用代码中的控制流将会继续。在实践中你将会经常调用show()并且在调用show()的函数最后,控制返回主事件循环。
“半模式”对话框是立即把控制返回给调用者的模式对话框。半模式对话框没有它们自己的事件循环,所以你将需要周期性地调用QApplication::processEvents()来让这个半模式对话框有处理它的事件的机会。进程对话框(例如QProgressDialog)就是一个实例,在你想让用户能够和进程对话框交互的地方那个,例如撤销一个长期运行的操作,但是需要实际上执行这个操作。半模式对话框模式标记被设置为真并且调用show()函数来被显示。

默认按钮

对话框的“默认”按钮适当用户按下回车键或者换行键时被按下的按钮。这个按钮用来表示用户接受对话框的设置并且希望关闭这个对话框。使用QPushButton::setDefault()、QPushButton::isDefault()和QPushButton::autoDefault()来设置并且控制对话框的默认按钮。

扩展性

扩展性是可以用两种方式来显示对话框:一个局部对话框用来显示通常最常用的选项,和一个显示所有选项的完全对话框。通常可扩展的对话框将初始化为一个局部对话框,但是有一个“更多”按钮。如果用户点击这个“更多”按钮,完全对话框将会出现。扩展性是由setExtension()、setOrientation()和showExtension()。

返回值(模式对话框)

模式对话框通常用在需要返回值的地方,例如需要分清用户按下“OK”还是“Cancel”。对话框可以通过调用accept()或reject()槽来被关闭,并且exec()将返回适当的Accepted或Rejected。exec()返回这个对话框的结果。如果窗口还没有被销毁,这个结果也可以通过result()得到。如果WDestructiveClose标记被设置,那么当exec()返回时,对话框被删除。

实例

模式对话框。

QFileDialog *dlg = new QFileDialog( workingDirectory,
                QString::null, 0, 0, TRUE );
        dlg->setCaption( QFileDialog::tr( "Open" ) );
        dlg->setMode( QFileDialog::ExistingFile );
        QString result;
        if ( dlg->exec() == QDialog::Accepted ) {
            result = dlg->selectedFile();
            workingDirectory = dlg->url();
        }
        delete dlg;
        return result;

非模式对话框。在show()调用之后,控制返回到主事件循环中。

 int main( int argc, char **argv )
    {
        QApplication a( argc, argv );
        int scale = 10;
        LifeDialog *life = new LifeDialog( scale );
        a.setMainWidget( life );
        life->setCaption("Qt Example - Life");
        life->show();
        return a.exec();
    }

半模式对话框的实例请参考QProgressDialog文档。

请参考QTabDialog、QWidget、QProgressDialog、GUI Design Handbook: Dialogs, Standard、抽象窗口部件类和对话框类。


你可能感兴趣的:(QT)