Qt中的每一个类,都有一个对应的同名头文件,其中包含其类定义。例如要使用QApplication类,则须要在程序中添加" #include <QApplication>"html
QApplication类用于管理应用程序范围内的资源。其构造函数须要main函数的argc和argv做为参数。java
widget被建立时都是不可见的(always created hidden)。widget中可容纳其它widget。linux
Qt中的widget在有用户行为或状态改变时会emit signal。 signal能够和slot函数链接在一块儿(connect),这样当有signal被emit时,对应的slot函数会被自动调用。git
QWidget类的构造函数须要一个 QWidget * 指针做为参数,表示其parent widget(默认值为0,即不存在parent widget)。在parent widget被删除时,Qt会自动删除其全部的child widget。程序员
Qt中有三种Layout Manager 类: QHBoxLayout,QVBoxLayOut,QGridLayOut。基本模式是将widget添加进LayOut,由Layout自动接管widget的尺寸和位置。算法
启动Qt程序时能够经过 -style 参数改变程序的默认显式风格。sql
Chapter 2 Creating Dialogs数据库
2.1 Subclassing Dialog编程
Qt中全部dialog的基类是QDialog。QDialog派生自QWidget。windows
Qt中全部定义了signal或slot的类,在其类定义的开始处都要使用Q_OBJECT宏。Qt中的signal关键字其实是宏定义。相似的,slots关键字也是宏定义。
Qt所提供的类分为若干模块:QtGui,QtNetWork,QtOpenGL,QtSql, QtSvg和QtXml等。
QObject::tr() 函数将输入的字符串转换为其余语言(国际化)。对全部用户可见的字符串都使用tr()函数是一个良好的习惯。
Buddy:两个widget A和B,若A拥有快捷键,当用户按下该快捷键时,程序的输入焦点自动转移到B上,则称B是A的buddy。
QWidget::close() 是一个slot,其默认行为是使对应的widget隐藏不可见,但并不删除该widget。
Layout 中可包含widget和其余layout。经过嵌套使用QHBoxLayOut、QVBoxLayOut、QGridLayOut,能够构造很是复杂的dialog。值得注意的是:layout manager 类并不属于widget。实际上,它派生自QLayout,而QLayout又派生自QObject。
QWidget::sizeHint() 返回一个widget()的理想大小(ideal size)。
emit关键字是Qt特有的,用于产生signal。
MOC(Meta-Object-Compiler): 对于全部使用了Q_OBJECT宏的类,在编译时都须要经过MOC的处理,不然会出现连接错误。解决该错误的办法也很简单,从新执行qmake以更新makefile,而后从新编译。
2.2 Signal and Slot in Depth
Signal & Slot 机制是Qt的根基。
Slot和普通的C++类成员函数几乎彻底一致;能够是virtual的,能够被重载,能够是public、protected或private的,并且也可因此被其余成员函数直接调用。
signal与slot之间的关联能够是一对1、一对多或多对一。
signal和signal之间也能够被关联,此种状况与signal-slot的区别在于,当第一个sigal被emit时,第二个signal也被emit。
能够调用disconnect()来解除signal 与slot之间的关联,一般状况下不多须要显式调用disconnect(),由于对象被删除时Qt会自动移除与其相关的关联。
sigal-slot或signal-signal这样的关联,要求两者具有相同的参数列表;若signal比slot中的参数多,多余的参数会被忽略。
思惟定势:signal-slot机制只能用于widget。实际上signal-slot机制是由QObject实现的,并不只局限于GUI编程,能够用于任何QObject子类。
2.3 Rapid Dialog Design
使用Qt Designer建立的form最终被转换为C++代码。
qmake工具可以检测到interface file(*.ui files),并调用uic,即Qt的user interface compiler。uic将.ui文件转换为C++代码,并存放在形式为ui_xxx.h的文件中。该文件中给出了dialog对应类的完整定义,并包含一个 setupUi()成员函数,用于初始化form。
注意,由uic建立的这个类未派生自任何Qt class。
Qt的 parent-child机制是由QObject实现的。当建立一个对象时若指定了parent,则parent将该对象添加至其 childern list。当parent被删除时,Qt会遍历其childern list并删除每一个child,该过程会递归进行。这一机制极大的简化了内存管理,下降了内存泄露的风险——程序员只需显式的删除经过new建立而且没有parent的对象。
对于widget,parent还有一层附加的意义:chidl widget 是显示在parent widget的范围以内的。若是删除parent widget,不只child widget从内存中被释放,在屏幕上也会消失。
QDialog::accept() 将dialog的返回值设为QDialog::Accepted(值为1),而QDialog::reject()将返回值设为QDIalog::Rejected(值为0)。
2.5 Dynamic Dialogs
Dynamic Dialog指的是程序在运行时根据.ui文件建立的dialog。这样的dialog不是经过uic将.ui转换为C++代码,而是在运行时使用QUiLoader类装载.ui文件。
可使用QObject::findChild<T> ()来访问form的child widget。
要使用QUiLoader,须要在Qt程序的.pro文件中添加如下内容: CONFIG += uitools
Dynamic dialog容许在不从新编译程序的前提下更改form的布局。
Chapter 3 Creating Main Windows
3.1 Subclassing QMainWindow
应用程序的主窗口是经过建立QMainWindow的派生类来完成的。QMainWindow和QDialog同样,都是派生自QWidget。
closeEvent()是由QWidget提供的一个虚函数,在用户关闭窗口时会被自动调用。
setCentralWidget()将某个Widget设置为主窗口的central widget, 而central widget意味着在显示时会占据主窗口的中央位置。
Qt下的GUI 编程支持多种图形格式。可使用多种方式为应用程序提供图像,最多见的包括:
1). 将图像存储在文件中,运行时加载之。
2). 在源码中include XPM文件(XPM文件也是合法的C++文件)。
3). 利用Qt的资源机制。
Qt的资源机制比之运行时加载更方便,并对全部支持的图像格式都能良好工做。
为了利用Qt的资源机制,须要建立一个资源文件,并在.pro文件中对应添加一行来对资源文件进行标识。例如:
RESOURCES= spreadsheet.qrc
资源文件自己采用了简单的XML格式。它被编译进程序的可执行文件,所以不会被丢失。在对资源进行定位时,使用路径前缀":/",例如“ :/images/icon.png "。资源自己能够是任何类型的文件。
3.2 Creating Menus and Toolbars
Qt经过引入Action这一律念简化了对menu和toolbar的编程。一个Action能够被添加到任意数量menu和toobar中。
在Qt中对menu和toolbar的编程涉及到三个步骤:
1). 建立并设置Action
2). 建立menu,并在其中添加Action
3). 建立toolbar,并在其中添加Action
Action的建立是经过QAction类来实现的,对每一个Action,能够为其设置accelerator,parent,shortcut key, 可见性以及status tip等属性,并能够经过调用connect()为ACtion设置被触发要执行的操做。
QTableWidget的基类QAbstraceItemView提供了selectAll()这个slot。
QApplication类提供了aboutQt()这个slot,能够经过全局变量qApp(一个类型为QApplication *的指针)来使用之。
在Qt中,menu由QMenu类的实例表示。而Qmenu是要被放入QMenuBar之中的。函数QMainWindow::menuBar()返回一个类型为QMenuBar * 的指针。QMenuBar::addMenu()根据指定文本建立一个QMenu widget并将其添加进MenuBar中。QMenu::addAction() 则为Menu添加Action。
任意Qt Widget均可以具有相关的一系列QAction。经过调用QWidget::addAction() ,能够为Widget添加Action。这一特性可用来建立上下文菜单。
3.3 Setting Up the Status Bar
QMainWindow::statusBar() 返回一个指向status bar的指针;status bar 在statusBar()第一次被调用时被建立。
3.4 Implementing The Menu
QMessageBox::Defalut修饰符使得被修饰的Button成为默认Button,而QMessage::Escape修饰符则使得Esc键自动触发被修饰的Button。
QMessageBox::warning()用于弹出提示对话框。该函数属于Qt提供的static convenicence function
static convenience function
QFileDialog::getOpenFileName() 可用于从用户处得到文件名——该函数弹出一个文件选择对话框,要求用户选择一个文件,并返回文件名,或者在用户选择"Cancel"时返回空字符串。该函数的第一个参数是其parent widget。对于dialog和其余widget,parent-child关系的意味是不彻底相同的。一个dialog永远是一个独立的窗口,可是若是它拥有parent,则默认在parent之上居中显示。
当用户发出关闭窗口的操做时,Qwidget::close() 这个slot会被调用,该slot向对应的widget发送close event。从新实现QWidget::closeEvent()可以拦截这个event,以便肯定是否真的要关闭窗口,防止误操做。
每一个QWidget都有一个windowModified属性,在窗口文档被修改时应该被设为True,不然被设为false。
QString::arg() 函数将字符串中编号最低的"%n"用参数进行替换,并返回替换后的字符串。
每一个Action均可以拥有一个类型为QVariant的关联数据。
Qt中的qobject_cast<T>() 机制对于动态库也能够正常工做。
3.5 Using Dialog
modeless window——one that runs independently of any other windows in the application
对于modeless dialog ,当其被弹出时,可能处于三种状况:
1). 这是该对话框第一次被激活
2). 该对话框以前曾被激活,但用户又将其关闭
3). 该对话框以前曾被激活,并且仍可见
show() 将一个隐藏窗口变为可见,而activateWIndow()则将窗口的状态变为active。
model window——pops up when invoked and blocks the application,preventing any other processing or interactions until it is closed.
一个dialog如果用show()来激活,则是modeless dialog;若经过exec()来激活,则是model dialog。此外,还能够调用setModel()来设置dialog的显示模式。
QDialog::exec() 的返回至在dialog被确认时为true,不然为false。
在栈上建立 model dialog是一种良好的编程惯例,由于在使用完后就再也不须要,而model dialog会在做用域结束后自动被销毁。
因为多数应用程序的About box 都是高度雷同的,Qt中提供了一个方便的static convenicence function QMessage::about(),该函数和QMessageBox::warning()很类似。
3.6 Storing Setting
Qt中是经过QSettings类来将应用程序的设置信息存储到平台相关的位置——windows下存入注册表中,unix中存在文本文件中。
QSettings的构造函数包含两个参数,分别是organization's name 和 application's name ,Qt使用这两个参数来对应用程序的设置信息进行定位。
QSettings以key-value pair的形式存储信息。
3.7 Multiple Documents
要想实现多文档程序,首先必需要经过new在堆上建立主窗口,而不是在栈上建立主窗口。
QAplication::closeAllWindows() 这个slot完成的操做是关闭应用程序全部的窗口,除非其中某个窗口拒绝了close event。程序员不须要担忧未保存的修改,由于这会由QWidget::closeEvent()负责处理。
经过在MainWindow的构造函数中调用setAttribute()函数来设置Qt::WA_DeleteOnClose属性,能够要求Qt在窗口被关闭时将其自动销毁
Qt在其可用全部平台上都支持SDI和MDI程序的建立。
3.8 Splash Screnns
在Qt中为程序添加splash screen很是简单,可经过QSplashScreen类来实现。
一般状况下,与splash screen相关的代码都放在main()中,出如今调用QApplication::exec()以前。
Chapter 4 Implementing Application Functionality
4.1 The Central Widget
QMainWindow的中央区域能够被任何类型的widget占据。
4.2 Subclassing QTableWidget
QTableWidget会自动建立QTableWidgetItem来存储用户的输入。
QTableWidgetItem类并非widget,而是一个纯粹的data class。
QTabeWidget::setItermProtype()能够设置在得到用户输入的状况下自动建立哪一种cllass。
4.3 Loading and Saving
QFile & QDataStream
QFile的析构函数负责将打开的文件关闭。
QDataStream类具备很强的通用性,可做用于QFile,QBuffer,QProcess,QTcpSocket,QUdpSocket。
Qt还提供了一个QTextStream类用于专门读写文本文件。
4.6 Subclassing QTableWidgetItem
每一个QTableWidgetIterm中可存储若干数据,这是经过个QVariant来实现的。每个QVariant对象都以某个role来存储某一类数据,经常使用的role有Qt::EditRole和Qt::DiaplayRole。
QVarinant对象能够存放多种类型的变量值,并提供向其余类型转型的函数接口。
使用默认构造函数建立的QVariant对象被视为invalid variant。
Chapter 5. Creating Custom Widgets
用户自定义的控件能够经过继承现有的Qt控件实现,也能够直接从QWidget继承来实现
5.1 Customizing Qt Widgets
5.2 Subclassing QWidget
经过对QWidget进行派生,并从新编写其部分event handler来进行绘图和响应用户操做,程序员能够实现对widget的外观和行为的彻底控制。
Qt的内置Widget如QLabel、QPushButton、QTabelWidget等,就是以这种方式实现的。
宏Q_PROPERTY()用来为widget声明和添加自定义属性。
每一个属性的定义都对应一个数据类型(任何被QVarinat支持的类型均可以),一个read function以及可选的write function。
对于包含自定义属性的类,Q_OBJECT和Q_PROPERTY()这两个宏都是必备的。
QImage类以一种硬件无关的方式存储图像信息。
Qt中提供了两个类型用于存储色彩信息:QRgb和QColor。
QRgb实际上是一个typedef,用于存放32-bit的像素信息。
QColor则是一个提供了许多接口函数的类,在Qt中普遍的用于存储色彩。
QWidget::update()函数用于对widget进行强制性的重绘。
QWidget::updateGeometry()用于告知包含该widget的layout:该widget的size hint已发生变化,layout会自动进行调整。
经过调用QWidget::update()和QWidget::repaint(),能够强制性的产生一个 paint event,二者的却别在于repaint()致使当即重绘,而update()只是将一个paint event放入event queue中。
若是对update()进行连续屡次调用,Qt会将连续的paint event压缩合并为一个paint event,以防止图像抖动。
每一个widget都拥有一个palette,用于设置widget中在什么状况下使用什么色彩,如背景色、文本色等。
widget的palette由三个color group组成:active ,inactive ,disabled。
QWidget::palette()以QPalette的形式返回widget的palette,而clolor group则经过枚举类型QPalette::ColorGroup指定
5.3 Intergrating Custom Widgets with Qt Designer
要像在Qt Designer中使用自定义widget的话,必需要让Qt Designer可以了解到它们的存在。
有两种机制:promotion approach &plugin approach
promotion approach 很容易也很省时,但缺点是自定义widget的自定义属性在Qt Designer中是不可见和不可访问的,而使用plugin approach时则不存在这些问题。
plugin approach要求建立一个Qt Designer 能够在运行时加载的plugin library,以用于建立widget的实例。因为Qt的MOC机制,Qt Designer能够动态获取widget的property list。
要使用plugin approach ,首先要对QDesignCustomWidgetInterface进行派生,并重写某些虚函数。
Q_INTERFACES()宏用于告知Qt该类实现了哪一个interface。
在实现plugin class的源文件尾部,必须使用Q_EXPORT_PLUGIN2()宏使得该plugin对Qt Designer可见、可用。该宏第一个参数是plugin的名字,第二个参数是实现该plugin的class name。
5.4 Double Buffering
QWidget::style()返回用于绘制该widget时所使用的style。Qt中的style都是QStyle的派生类。同一应用程序中的 widget通常都使用相同的style,然而能够调用QWidget::setStyle()来进行widget层次的特别设置。
Chatper 6 Layout Management
6.1 Laying Out Widgets on a Form
Qt提供的的基本的Layout Manager包括:QHBoxLayout,QVBoxLayout,QGridLayout和QStackLayout。
Qt中其它能完成Layout management功能的类包括 QSplitter,QScrollArea,QMainWindow和QWorkspace。
Qt中管理child widget的layout共有三种方式:absolute positioning, manual layout和layout managers。
Absolute positioning:即由程序员经过hard-coded的形式管理child widget的位置和尺寸。
Manual Layout:child widget的位置依然由程序员经过hard-coded的方式肯定,而尺寸与父窗口的大小成必定比例,而不是彻底的hard-coded。这种方式经过对form的resizeEvent()进行再实现来对child widget的定位。
最重要的三个Layout Manger是QHBoxLayout,QVBoxLayout,QGridLayOut,他们都是派生自QLayout
QGridLayout的使用略微有些复杂,它工做在一个由Cell组成的二维grid上。对于QGridLayout,为其添加widget的方式以下:
layout->addWidget(widget,row ,colum,rowSpan,columnSpan)
其中widget为待添加的child widget, row和clomun肯定该widget所占据空间中左上角那个Cell的位置坐标,rowSpan和columnSpan则指定widget的大小,这两个参数的的缺省值为1。
addStretch()向Layout Manager中添加“占位符”。
每一个widget都有本身的size policy,由其告知layout 系统如何处理该widget外形上的stretch或是shrink。Qt中widget的size policy是经过QSizePolicy类来表示的。每一个QSizePolicy由水平和竖直两组size policy组成,最多见的值包括:
Fixed Minimum Maximum Prefered Expanding
除了上述两组size policy外,QSizePolicy中还存储水平和竖直方向的stretch factor,该值用来代表在form尺寸扩展时widget随之扩展的比率。
6.2 Stacked Layouts
QStackLayout 类能够管理多个page,但每次只显示其中之一,而将其余page向用户隐藏。
QStackLayout类自己是不可见的。
为了方便起见,Qt包含有QStackedWidget类,即一个内置了QStackedLayout的QWidget。
6.3 Splitter
类QSplitter是一个能包含其余widget的widget。QSplitter中包含的widget按顺序排列,并被splitter handle相互分隔开。
QSplitter经过构造函数中的参数来来决定是水平方向仍是竖直方向。
不一样于前面介绍的Layout Mangener们只是负责处理widget的layout而自身没有可视化的表示,QSplitter派生自QWidget,所以能够同其它widget同样的被使用。
QSplitter类提供了存储自身状态的两个函数:savestate()和restorestate()。
6.4 Scrolling Areas
QScrollArea类提供了1个可滑动的viewport和2个滑动条。
QScrollArea的使用方法是调用其提供的setWidget()函数,将但愿为其添加滑动条的widget添加。QScrollArea自动将添加进来的widget的parent设定为其viewport,而其viewpoint可经过QScrollArea::viewport()进行访问。
QScrollArea的多数功能是经过继承QAbstraceScrollArea类而得到的。而诸如QTextEdit和 QAbstractIterView这样的类是派生自QAbstractScrollArea的,所以不须要将其用QScrollArea类包裹起来以得到scroll bar。
6.5 Dock Widgets and Toolbars
在Qt中,dock widget是经过QDockWidget类来实现的。
每一个dock widget都有本身的title bar。
从Qt4开始,toolbar拥有本身专属的显示空间,而再也不是如以前的版本中容许dock widget与其分享。
对一个widget调用setAllowedArea()能够指定容许在那些dock areas上放置该widget。
6.6 Multiple Document Interface
Qt中,编写MDI程序是经过使用QWorkspace类,将其做为程序的central widget,并把每一个文档窗口都做为QWorkspace的child。
Qt程序的命令行参数中与Qt相关的参数,会由QApplication的构造函数负责自动移除掉,不会传递给Qt程序的main()函数。
Chapter 7 Event Processing
Qt中大多数event都是做为对用户操做的响应而产生的,但也有一些是系统内部独立生成的。
在使用Qt进行编程时,一般不多须要考虑event,由于Qt中的widget会在有重要事件发生时emit signal。Event在咱们要编写自定义widget或是要修改现有Qt widget的特性时,则变得很重要。
不要在概念上将event和signal两个概念混淆。当程序员操纵使用widget时,signal是须要关注的对象;而当程序员须要实现一个widget时,event则是须要关注的对象。
例如,当使用QPushButton时,咱们更关注其提供的clicked() signal 而不是致使该signal被QPushButton emit的低层次的鼠标或键盘event。可是若是咱们要本身实现一个相似于QPushButton的类,那就轮到咱们编写代码来处理鼠标和键盘操做,并在必要时emit clicked() signal。
7.1 Reimplementing Event Handles
Qt中,每一个event都是一个派生自QEvent的对象。Qt中包含超过100种的event,每种event有一个enum value进行标识。
QEvent::type()返回event type。
全部event都是经过其对应类中的event()函数,来向对象发送通告的;这个event()函数继承自QObject。QWidget中 event()的实现是将最多见类型的event转发给对应的event handlers,例如mousePressEvent(),keyPress-Event,painEvent()。
键盘event对应的event handler是keyPressEvent()和keyReleaseEvent()。
QKeyEvent::modifiers()
Tab键和Shift+Tab键的处理有些特殊,它们不是由keyPressEvent()负责处理,而是在QWidget::event()中进行处理,并且发生在调用keyPressEvent()以前,处理方式是将输入焦点按照链中的顺序向后或向前传递。
实现key binding的更高层次的实现方式是利用QAction。QAction类内部使用了QShortCut类来实现key binding。
QObject::startTimer() 用于建立定时器,并返回相应的ID。QObject支持多个定时器同时存在。
killTimer()用于销毁定时器,参数为定时器的ID。
Timer event位于底层,实现定时功能更简单的方式是使用QTimer类,QTimer会按期的emit timeout() siganl。
7.2 Installing Event Filter
Qt的evnet model中很强大的一个特性,就是能够设定某个对象来监控另一个对象,后者全部的event在对其可见前都要先经过前一个对象的监控和处理。
Qt中设置event filter涉及到两步操做:
1. 在要被监控的对象中调用installEventFilter() 来完成对监控者的注册。
2 在监控者的eventFilter() 中对被监控者的event进行处理。
Qt的event model中,一个event若未获得处理(event handler的返回值为false),则Qt会负责将该event向上传递,即交由其parent负责处理
Qt中对event的处理能够分为下面5个层次:
1. 对某个特定的event handler进行重实现
2. 对Widget中的QObject::event()进行重实现,这样在event被传递给特定的event handler以前就获得了处理。
3. 为某个对象安装event filter
4. 为QApplication object安装filter,这样能够监控应用程序中全部对象收到的全部event,在进行debug是很是有用。
5. 派生QAapplication的子类,并对notify()进行重实现。Qt调用notify()来发送event的。这是捕获全部event的惟一方法。
7.3 Staying Responsive During Intensive Processing
在完成耗时操做的同时,还要保证程序可以对用户操做正常相应,常见的解决机制是多线程。
一种简单的解决方案是在耗时操做的过程当中有规律的调用QApplication::processEvnets()。该函数通知Qt处理pending events,处理结束后再将控制权返回至调用者。
Qt中的进度对话框是由QProgressDialog类来实现的。
除了使用多线程和进度对话框外,还存在一种彻底不一样的处理耗时操做的方法:操做只在程序空闲(无用户交互)时再进行,而不是马上开始执行。
这种方法能够利用0-milisecond timer来实现,每次定时器触发时,检查是否有pending event,若无则继续好事操做,如有则处理event,完成与用户的交互。
Chapter 9 Drag and Drop
9.1 Enabling Drag and Drop
默认状况下,QTextEdit这个widget接受来自于其余程序的文本拖拽的;若是用户将一个文件拖拽至其上,它会将文件名插入显示文本。能够调用setAcceptDrops()来容许或禁止接受拖拽。
dragEnterEvent() ,该函数在用户将一个对象拖(drag)至widget之上时被调用,其参数为QDragEnterEvent类型的指针。
默认状况下,widget不接受用户的拖拽行为;若对该指针调用acceptProposedAction(),则是告知Qt容许该widget接受用户的拖拽行为,Qt会经过改变鼠标形状来提示用户。
dropEvent() ,该函数在用户将一个对象拽(drop)至widget之上时被调用,参数为QDropEvent类型的指针。
QWidget类还提供了dragMoveMent()和dragLeaveEvent()这两个函数,但对于大多数应用而言不须要对其进行再实现。
mousePressEvent(): 鼠标被按下时该函数被调用
mouseMoveEvent() :鼠标保持按下的状态且移动时,该函数被调用。
QDrag类使用QMimeData类来存储与拖拽操做相关的信息。
9.2 Supporting Custom Drag Types
能够从如下三种机制中进行选择:
1. 在源这一边调用QMimeData::setData(),将信息存储在QByteArray中,而在接受者这一方,调用QMimeData::data()将信息提取出来。
2. 对QMimeData进行派生,在子类中对formats()和retrieveData()这两个函数进行从新实现,来处理自定义数据。
3. 若是拖拽动做发生在一个应用程序的内部,那么能够对QMimeData进行派生,将信息存储在该子类中。
QMimeData::formats()返回其支持的MIME类型列表。
QMimeData::retrieveData()将某个指定MIME类型的数据以QVariant的形式返回。QMimeData所提供 text()、html()、urls()、data()等接口函数,都是依靠retrieveData()来完成底层操做的。
9.3 Clipboadr Handling
Qt中经过QApplication::clipboard()来得到对QClipboard的指针。对系统clipboard的写操做经过 setText(),setImage或setPixmap()完成,而读操做则经过text(),image()和pixmap()来完成。
QClipboard::setMimeData()
QClipboard::MimeData()
QClipboard::supportsSelection() 在X11平台下返回true,其余环境下返回fasle。
若是但愿每当clipboard中的内容发生变更时收到通知,能够利用Qt提供的QClipboard::dataChanged()这个slot
Chapter 10 Item View Classes
MVC机制:Model-View-Controller
Qt中提供一种模仿MVC的model/viewer机制。
Qt中的delegate这个抽象概念与Controller略微不一样,它负责为item的生成和编辑提供良好的控制。
Qt为每种类型的view都提供了默认的delegate,这对于大多数应用程序已经足够了,一般状况下程序员不须要考虑delegatd的问题。
能够为一个model注册两个或更多的view,Qt自动保持多个view之间数据的同步和一致性,当数据在某个view中被修改后,会自动在其余相关view中反映出来。
大多数状况下,程序向用户提供的item的数量并不庞大,所以能够简单的使用Qt内置的item view clas(QListWidget,QTableWidget和QtreeView),而没有必要使用Qt提供的model/view 机制。但对于成员数量很大的数据集,采用model/view机制则是明智的选择。
10.1 Using Item View Convenience Classes
QListWidget
QListWidget中包含多个roles,每一个都与一个QVariant变量关联。最经常使用的roles由Qt::DisplayRole,Qt::EditRole,Qt::IconRole
对于上述role,Qt都提供了方便的接口函数用于读写数据。程序员还能够对role进行自定义,经过Qt::UserRole或更大的数值进行标识。
默认状况下QListWidget是只读的,须要调用QAbstractItemView::setEditTriggers()为其设定能引起编辑操做的动做。
QTableWidget
默认状况下QTableWidget是容许编辑的,能够调用QAbstractItemView::setEditTriggers(QAbstractItemView::NoEditTriggers)来禁止编辑。
QtreeWidget
默认状况下,QtreeWidget是只读的。
10.2 Using Predefined Models
Qt提供的预约义model有如下几种:
QStringListModel 存储一组字符串
QStandardItemModel 存储任意层次结构的数据
QDirModel 对文件系统进行封装
QSqlQueryModel 对SQL的查询结果集进行封装
QSqlTableModel 对SQL中的table进行封装
QSqlRelationalTableModel 对带有foreign key的SQL table进行封装
QSortFilterProxyModel 对另外一个model执行sort and/or filter
model中存放的每项数据都有相应的"model index",由QModelIndex类来表示。
每一个index由三个部分构成:row,column和代表所属model的指针。对于一维的list model,column部分永远为0。
在model/view 机制中,对数据的操做都是经过model执行的,而model负责保证在数据发生变更时view自动更新。
QDirModle::mkdir()——建立文件夹的工做能够经过QDir类来完成,不过QDirModel提供了工做于QModelIndex之上更方便的函数。
不一样于其它model,QSortFilterProxyModel对一个已有的model进行封装,并完成数据在底层modle和view之间的传递。
setSourceModel()
10.3 Implementing Custom Models
model中的每项数据都有对应的index和一组称为“role”的属性,其中最经常使用的有Qt::DisplayRole和Qt::EditRole。
对于list和table这两类model,其中每一个元素的parent都是root,即表示为一个invalid QModelIndex。
而对于tree model,某些元素的parent为root,而有些元素的parent为model中的其它元素。
Qt中提供了几种model 基类,包括 QAbstractListModel,QAbstractTableModel和QAbstractItemModel ,其中QAbstractItemModel是另外二者的基类,用于支持范围很广的modles,包括具有递归层次结构的;而 QAbstractListModel用于支持一维数据集,QAbstractTableModel用于支持二维数据集。
实际上要建立只读的自定义model的话,并非件困难的事情。
要自定义只读的table model的话, 须要从新实现rowCount(),columnCount()和data()这三个函数。
createIndex() 用于建立并返回一个model index
flags()被model用于表示能够对数据执行的操做(例如,是否可编辑),从QAbstractTableModel继承而来的默认实现是返回Qt::ItemSelectable | Qt::ItemIsEnabled
qDeleteALl()对一个含有指针的容器进行迭代,并对其中每一个指针元素执行delete操做。
10.4 Implementing Custom Delegates
view中的每一个item是由delegate负责显示和编辑的,在大多数状况下,view默认的delegate足够知足用户需求。
setItemDelegate() 为view指定其delegate。
QItemDelegate & QAbstractItemDelegate
要提供一个容许编辑的自定义delegate,咱们必须对createEditor(),setEditorData(),setModelData()进行从新实现,并且还必须从新实现paint()以改变item的显示。
QTimeEdit & QTime
当用户开始编辑操做时,view会调用createEditor()来建立一个Editor,而后调用setEditorData()来用item的当前值对Editor进行初始化。
Chapter 11 Container Class
Qt提供的容器类的最大优势在于平台无关性和隐式共享特性
11.1 Sequentail Containers
QVector<T> array-like data structure(在尾部插入数据时效率很高,而在中间和头部插入数据时开销很大)。
QVector提供了[]运算符
QVector能够用<<运算符代替append()函数。
QVector中的基本类型及指针被初始化为0。
QLinkedList<T>
QLinkedList不提供[]运算符,因此必须经过迭代器来对其进行遍历。
QList<T> array-list:综合了QVector<T>和QLinkedList<T>最重要的优势: 支持[]运算符
在头部或尾部的插入/删除操做很迅速,而尺寸在1000如下时,在中间的插入/删除操做也很迅速。
一般状况下,QList是最合适的通用型容器。
QStringList: QList<QString>的子类,在Qt中的API中被普遍使用
QStack<T> 和QQueue<T>是Qt提供的两个convenience subclasses,QStack<T>其实是一个额外提供了push(),pop()top()接口的QVector,而 QQueue<T>实际是一个额外提供了enqueue()和dequeue()和head()的QList。
容器中能够放置的类必须拥有default constructor、copy constructor 和 assignment operator(显式定义或由编译器生成)
注意,派生自QObject的类不符合上述要求,由于其不具有copy constructor和assignment operator;解决方法是在容器中存储对象指针而不是对象自己。
容器中所存放的元素自己也能够是容器,便可以嵌套——不过须要注意将连续的尖括号用空格分隔开,以避免编译器误认为>>运算符。
Iterator
Qt支持两种风格的迭代器——Java-style和STL-style
Java-style的迭代器更容易使用,而STL-style的迭代器能够同Qt和STL中的算法联合使用,更为强大。
Java-style Iterator
每一个sequential容器类,都有两个Java-style的迭代器类型:只读迭代器和读写迭代器。
在使用Java-style的迭代器时,要清楚的第一件事情就是:迭代器并不直接指向容器中的元素,而是指向元素以前或以后的位置。迭代器被初始化时指向容器中第一个元素以前;若迭代器的右侧有元素存在,hasNext()函数返回true;next()函数返回位于迭代器右侧的元素,并将迭代器向右方移动一个元素的位置;hasPrevious()和previous()函数执行反方向的操做。
remove()函数老是删除最近一次被跳过的那个元素。
setValue()函数老是对最近一次被跳过的那个元素执行更新操做
insert()函数在迭代器当前指向的位置处插入新元素,并将迭代器指向新元素及其后续元素之间的位置。
STL-style Iterator
每一个sequential容器类,都有两个STL-style的迭代器类型:Container<T>::iterator和Container<T>::const_iterator。
容器的begin()函数返回一个指向容器中头部元素的iterator,而end()返回指向容器中尾部元素以后位置的iterator;
在容器为空时,begin()和end()的结果相同。
一般经过调用isEmpty()来检查容器是否为空,而不是经过比较begin()和end()的结果。
能够对STL-style的iterator使用+、-、*这三个运算符,相似于指针的用法。
某些Qt函数的返回值是容器类;若是须要使用STL-style的迭代器来对这样的返回值进行遍历,必须保存返回值的一个副本,并在副本上完成遍历,不然会可能会致使所谓的"dangling iterator"。
注意,若使用java-style的只读迭代器,在这种状况下会隐式的完成复制的工做,保证迭代器老是在副本上进行遍历操做。
implicit sharing(copy on write)
Qt中的implicit sharing机制的美妙之处在于它鼓励程序员在返回对象时采用传值这种简明的方式而不是引用或指针。
STL与此相反,鼓励程序员使用non-const引用来传递vector以免将函数返回值的复制开销。
Qt中全部的容器都采用了implicit sharing机制;此外不少其余类QByteArray,QBrush,QFont,QImage,QString也采用了该机制——这保证这些类在以传值方式进行传递时有很高的效率,不管是做为参数仍是函数返回值。
在Qt提供的implicit sharing机制下,对vector或list执行只读操做时,采用at()而不是[]运算符是一个更好的选择。
相似的,尽量的使用constBegin()和constEnd()以免没必要要的拷贝操做。
foreach syntax
foreach在进入循环体时自动复制容器的副本并在此副本上进行迭代,所以若是迭代过程当中有经过迭代器对容器的修改操做的话,并不会影响循环的进行,循环结束后容器的内容也不会发生变化。
固然,若是在foreach循环中直接使用[]运算符对容器进行写操做的话,容器内容天然会发生变化。
foreach中支持break和continue语句
11.2 Associative Containers
QMap
QMap中的key-value对是升序排列的
插入和删除操做中均可以使用[]运算符,其下标为key;为避免建立没必要要的空值,推荐用vlaue()而不是[]从QMap中取值。
QMap<K,T>中的K和T除了要求具有默认构造函数、拷贝构造函数和赋值运算符外,K还必须支持operator <,由于这样才能实现前面提到的升序排列。
keys() & values()
QMap的特性是单值;QMultiMap<K,T>则支持同一关键字下多值的存在,插入操做由insertMulti()完成
QHash
QHash提供的接口和QMap很类似
QHash<K,T>中的K要符合的额外要求:支持operator ==,而且K可用全局函数qHash()来计算hash value
QHash一般是单值的,而QMultiHash则经过insertMulti()支持多值插入。
QCache<K,T> & QSet<K>
遍历associative containr的最简单方法是使用Java-style的迭代器
foreach syntax也可用于assocaitive container
11.3 Generic Algorithms
头文件<QAlgorithms>中声明了一组全局模板函数,用于实现做用于容器的基本算法;多数算法都经过STL-style的迭代器来完成。
STL头文件<algorithm>中的函数,便可做用于Qt容器,也可做用于STL容器。
qFind(),qBinaryFind(),qFill(),qCopy(),qSort(),qStableSorg(),qDeleteAll(),qSwap()
须要注意的是,qDeleteAll()只对包含指针的容器有意义,该函数将释放全部对象,但并不删除容器中的指针。
11.4 Strings,Byte Arrays,and Variants
QString,QByteArray和QVariant这三个类和容器类有不少类似之处,在某些场合下可做为容器类的替代品;和容器类同样,这三个类也应用了implicit sharing 机制
QString
QString中存放的是16-bit的Unicold值。
从概念上,QString能够当作是QVector<QChar>。
QString提供+/+=运算符以及append()函数用于合并字符串
QString提供的sprintf()函数,与C++标准库中的sprintf支持相同的参数格式。
QString提供的arg()函数,是比sprintf()更好的选择,由于它保证类型安全,支持unicode。
QString::number(): number->string
QString::setNum(): number->string
反向的转换接口:toInt(),toLongLong(),toDouble()等,这些函数都有一个可选参数——bool类型的指针,若转换失败则将该bool变量置为true,不然置false.
mid()函数返回指定区间内的子串;left()和right()函数则分别返回左子串和右子串
QString的indexOf()函数可用于文本匹配,返回所匹配子串的起始下标;匹配失败时返回值为-1。
startsWith()和endsWith()函数可用于判断字符串的首部和尾部是否符合某种模式。
QString在进行比较时是大小写敏感的;当所比较的字符串是用户可见时,使用localeAwareCompare()一般是正确的选择。
toLower() & toUpper()
replace() & remove() & insert()
trimmed() & simplified()——这两个函数用于消去字符串中的whitesapce(spaces,tabs,newlines等)
QString::split()将一个字符串分割为子串,并返回由这些子串们组成的一个QStringList。
QStringList中的全部元素能够经过join()函数组成一个新的字符串,join()的参数在合并时会被插入相邻元素中间。
在大多数状况下,由const char * 至QString的转换是自动的。
反方向的转换,可经过toAscii()或toLatin1()来完成;这些函数返回一个QByteArray,其能够经过QByteArray::data或QByteArray::constData()来转换为const char * ;
为了简化转换,Qt提供了一个qPrintable()宏用于完成与toAscii()以及constData()相同的操做。
QByteArray
QByteArray提供的API与QString的很类似。
QByteArray的用处在于存储原始2进制数据及8-bit编码的字符串。
一般选择QString而不是QByteArray来存储文本信息,由于QString支持Unicode。
QByteArray会自动在最后一个元素以后补上‘\0',这样使得将QByteArray传递给须要const char *的函数变得很容易。
QByteArray支持'\0',容许存储任意的2进制数据。
QVariant
QVariant类可用于存放不少Qt类型的值,而且还能够存放容器。
QVariant在 item view class,database model 和QSetting中被普遍的使用着。
利用QVariant和嵌套,能够建立很是复杂的数据结构。
QVariant的便利性是以性能和代码的可读性为代价的。
QVariant被Qt的meta-object system所使用,所以是Qtcore module的组成部分。
QVariant也能够支持用户自定义的数据类型,前提是该类型具备defalut constructor和copy constructor。要实现对用户自定义类型的支持,须要使用宏Q_DECLARE来注册该类型。
全局函数:qVariantFromValue(),qVariantValue<T>(),qVariantCanConvert<T>()
Chapter 12 Input/Output
Qt经过QIODevice类,对支持块读写的设备进行了强有力的抽象和封装,从而提供了良好的I/O支持。
Qt中提供的QIODevice的子类包括QFile,QTemperorayFile,QBuffer,QProcess,QTcpSocket和QUdpSocket.
QProcess,QTcpSocket和QUdpSocekt属于sequential device,即数据只能被访问一次,且只能按照顺序读取;而QFile、QTemporaryFile和QBuffer属于random-access device,数据能够被访问任意次,且能够从任意位置开始读取;
除了以上的device class,Qt还提供了两个可用于读写任何设备的高层数据流类:用于二进制数据的QDataStream和用于文本的QTextStream。这两个类负责处理字节序和文本编码等问题,保证运行在不一样平台或不一样国家的Qt程序能正确读取彼此的文件。这使得Qt的I/O类比起C++标准库中的I/O类更方便——它将这些问题留给了程序员来处理。
QProcess容许程序员调用外部程序并经过标准输入流、标准输出流和标准错误流与其进行通信。默认状况下,进程之间的通信是异步的,但也能够在某些操做上阻塞。
12.1 Reading and Writing Binary Data
Qt中载入和保存二进制数据最简单的方法就是使用QFile来打开文件,并经过QDataStream对象来访问文件内容。
QDataStream对象的version number直接影响着Qt中的数据类型以何种方式表示和记录。C++的基本类型保证老是以同一种方式表示和记录,不受verson number的影响。
在使用QDataStream时,须要保证在读文件和写文件时使用相同的number version。
若QDataStream被用来单纯读写C++基本类型的数据,那么没有必要调用setVersion()来设定version number
QDataStream的默认字节序是big-endian,这能够经过调用setByteOrder()改变。
使用Qt时,一般没有必要显式执行关闭文件的操做,由于QFile在销毁的时候会自动执行文件的关闭操做。
能够调用flush()来强制完成数据的写操做。
QDataStream存储数据的方式能保证能够无缝的将其再次读出,例如,一个QByteArray对象的存放形式是一个32-bit的计数值后跟数据自己。
可使用readRawData()和writeRawData()来读写原始字节。
使用QDataStream读数据时的错误处理是很简单的,status()函数返回当前状态,包括QDataStream::Ok、QDataStream::ReadPastEnd和QDataStream::ReadCorruptData。
当有错误发生时,>>运算符的返回值老是0值或是空值。
为自定义类型提供>>和<<运算符有不少好处:能够对包含该自定义类型的容器使用QDataStream;能够将该自定义类型的数据经过QVariant保存起来,这须要先调用qRegisterMetaTypeStreamOperators<T>()宏。
若是但愿一次性读/写文件,能够放弃QDataStream与QIODevice子类的联合使用,而用QIODevice提供的write()和readAll()接口来直接进行读写操做。
QIODevice()提供的peek()函数返回下一个待读取的数据,而不改变读写指针,该接口对于random-access device 和sequentail device 都适用。
对于random-access,可使用seek()来指定读写指针的位置。
12.2 Reading and Wrinting Text
Qt提供了QTextStream来读写文本格式的文件,如plain text,HTML,XML,source code等。
QTextStream负责完成Unicode与系统本地编码之间的转换,并自动处理不一样操做系统之间换行符不一样表示方式上的转换(如在windows下是\r\n,在linux是\n)。此外还自动完成C++基本数字类型与字符串之间的相互转换。
QTextStream使用QChar做为基本的数据单元。
写文本数据很是容易,然而读文本则可能很是具备挑战性,由于文本数据在本质上是具备歧义性的。
默认状况下,QTextStream使用本地编码来进行读写操做;能够调用setCodec()改变之,如stream.setCodec("UTF-8");
Qt仿照C++的I/O库为QTextStream也提供了stream manipulators。
同QDataStream同样,QTextStream在一个QIODevice之上进行操做;除此以外还能够在QString上进行操做,这种状况下不须要为流设置编码格式,由于QString老是Unicode。
若是只读写ASCII字符集和Latin-1字符集的文件,能够直接使用QIODevice提供的API进行读写,而不用使用QTextStream。一般这并非一个好主意,由于这不利于国际化和后期维护。
若是真的须要直接向QIODevice写文本,在使用open打开该QIODeveice时必须指定QIODevice::Text标志;该标志的做用在于告知QIODevice,在写文本数据时,在windows平台上将全部\n转换为\r\n;而在读文本数据时,在全部平台上忽略\r。
12.3 Traversing Directories
QDir提供了一种平台无关的方法用于获取文件信息以及遍历目录。
QDir::entryList() 其第二个参数代表要读取目录下的那些条目——文件(QDir::Files)仍是子目录(QDIr::Dirs)等。
QDir::separator() 返回当前平台上的目录分割符QDir在全部平台上都将'/'视为目录分割符,在windows平台上还额外的识别'\'。
QDir::currentPath() 返回程序当前目录的绝对路径
QDir::homePath() 返回用户的主目录路径
QFileInfo类容许程序员获取文件的属性信息,如大小,访问权限,全部者及各类时间戳等。
12.4 Embedding Resources
Qt容许在程序的可执行文件中嵌入二进制或文本文件,这是经过Qt的资源机制实现的。
12.5 Inter-process Communication
QProcess类容许运行外部程序并与其通信。该类异步工做,在后台完成相应的工做来保证UI对用户操做的正常响应,在外部程序终止或有数据产生时emit signal来通知本程序。
QProcess::start()用于传递必要的参数,并启动外部程序。
静态函数QProcess::execute()会运行一个外部程序并在外部程序结束以前保持阻塞状态。
QTemporaryFile::open()在无参数时以读写模式打开文件。
QTemporaryFile在对象生存期结束时会自动删除临时文件。
当QProcess以同步模式使用时,不须要创建signal-slot链接。
若是须要比静态函数execute()精度更高的控制,能够改用下面的方法:
首先建立一个QProcess对象,而后对其调用start(),以后调用QProcess::waitForStarted()强制进入阻塞状态,直至外部程序顺利启动为止,以后再调用QProcess::waitForFinished(),阻塞直至外部程序结束为止。
Chapter 13 Databases
QtSql模块提供了一个平台无关、数据库无关的访问SQL数据库的接口。
Qt中的每一个数据库链接用一个QSqlDatabase对象来表示;Qt使用不一样driver来和各类不一样数据库的API进行通信。
QSqlQuery提供了直接执行任意SQL语句的特性;此外还提供了两个高层次的无需SQL命令的数据库接口:QSqlTableModel和QSqlRelationalTableModel
13.1 Connecting and Querying
在执行SQL命令前,必须先创建好同数据库的链接。
静态函数QSqlDatabase::addDatabase()用于建立一个新的QSqlDatabase对象,函数的第一个参数指定了Qt该选择哪一个Driver来访问数据库。
对QSqlDatabase对象设定好host name,database name ,username和password后,须要调用open()函数创建到数据库的链接。
一旦到数据库的连接创建好后,就能够经过QSqlQuery::exec()来执行底层数据库所支持的任意SQL语句了。
QSqlQuery::next()返回查询结果集中的下一行,而QSqlQuery::value()则返回当前行中的某一项的值,以QVariant的形式返回。
可使用QSqlQuery::isActive()来检查SQL语句的执行是否出现错误。
Placeholder
QSqlQuery::prepare()
QSqlQuery::bindValue() or QSqlQuery::addBindValue()
QSqlQuery::exec()
Qt支持数据库中transaction(事务)这个概念。transaction()用于启动transaction,而commit()或rollback()用于结束transaction。
静态函数QSqlDatabase::database(),返回指定链接所对应的QSqlDatabase对象。
QSqlDatabase::driver() 返回该链接底层所使用的dirver
QSqlDatabase::hasFeature()可用来查询底层数据库是否支持某项特性。
Qt容许在一个程序中建立多个数据库链接,这种状况下在执行SQL语句时,须要为QSqlQuery对象的构造函数传入要执行该语句的数据库对应的QSqlDatabase对象。
与QSqlQuery相比,QSqlTableModel提供了一个更高层次、更抽象的接口,能够避免使用原始的SQL命令。
QSqlTableModel::record() & QSqlTableModel::value()
QSqlTableModel::insertRow() & QSqlTableModel::setData()
QSqlTableModel::submitAll() ,与其余model不一样,在使用QSqlTableModel时,必须调用submitAll()来强制全部的修改都写入数据库。
当须要处理外键foreign key时,须要使用QSqlRelationalTableModel而不是QSqlTableModel。
对于使用了SQL相关类的应用程序,须要在对应的.pro中添加下面一行:"QT +=sql",这样在连接时会将QtSql库链入。
Chapter 14. Networking
14.1 Wrinting FTP Client
QFtp是Qt提供的封装了ftp协议的一个类。
Qftp的所执行的操做是异步完成的,这保证了FTP命令在执行过程当中UI处于可相应状态。
当程序不须要链入QtGui库时,能够在main()中建立QCoreApplication对象而不是QApplication对象。
QCoreApplication::argurments() 函数以QStringList的形式返回程序的命令行参数,其中第一个参数为程序名,而且全部与Qt相关的参数例如-style都已经被移除掉了。
QUrl是Qt提供的一个用于从url中提取各类信息的一个高层接口。
提交的FTP命令被排队,并在Qt的event loop中被执行;QFtp对象在它处理完全部请求后会emit done(bool) 这个signal,其中类型为bool的参数代表是否有错误发生。
QFtp所封装的Ftp命令包括 connectToHost(),login(),close(),list(),cd(),get(),put(),remove(),dir(),mkdir(0,rmdir() 和rename()。 这些函数都返回一个标记命令的ID。
TransferMode()可用于改变传输模式,默认为passive;TransferType()用于改变传输类型,默认为binary。
此外,还能够经过rawCommand()来执行任意标准ftp命令,如 ftp.rawCommand("SITE CHMOD 755 fortune");
QFtp在开始执行每条FTP命令时都会emit commandStarted(int)这个signal,并在每条FTP命令完成时emit commandFinished(int,bool) 这个signal,其中int 参数是命令对应的ID,而bool参数则表示是否有错误发生。
QFtp在每当ftp链接的状态发生改变时会emit stateChanged() 这个signal,这里的状态包括QFtp::connecting、QFtp::connected、QFtp::LoggedIn等。
每当命令队列变为空时,QFtp都会emit done(bool)这个signal。
当有错误发生时,Qftp自动将命令队列清空。
若要使用QFtp,须要在项目的.pro文件中添加下面一行:" Qt +=network"
listInfo(),每当QFtp执行list()命令获得一个目录项时,都会emit 这个signal
QFtp的get()函数在调用时能够不给出要写的设备,这种状况下QFtp会在有新数据可用时emit readyRead()这个signal ,程序员能够调用read或readAll()这两个接口用于读数据。
14.2 Writing HTTP Clients
与Ftp协议相对应,Qt为Http协议提供了QHttp类。
QFtp和QHttp在接口和特性上有不少类似之处。
QHttp一样是异步工做模式。
QHttp在开始执行request命令时emit requestStarted(int)这个signal,而在request操做结束时emit requestFinished (int,bool)这个signal,其中int参数和bool参数的含义与QFtp中的相似。 当有错误发生时,请求队列被自动清空。与QFtp相同,QHttp也提供了readyRead()信号和read()、readAll()这两个接口函数。
14.3 Writing TCP Client-Server Applications
QTcpSocket和QTcpServer这两个类用于编写Tcp客户端和服务端
基于TCP的应用程序或者是line-oriented,或者是block-oriented。
QTcpSocket经过对QAbstractSocket的继承而成为QIODevice的子类,于是可使用QDatatStream或QTextStream来对其进行读写。
QTcpSocket在有数据可读时会emit readyRead()这个signal。
Qt中提供的forever syntax等同于 for( ; ; )
QTcpServer类容许接受外来TCP链接,每当检测到外来TCP链接请求时,会自动调用QTcpServer::incomingConnection()函数,参数为标识socket ID的int型变量。
QTcpSocket::listen()用于完成监听工做。
QTcpSocket提供的canReadLine()和readLine()这两个函数对于line-oriented应用程序提供了很大的便利。
14.4 Sending and Receiving UDP Datagrams
不一样于QTcpSocket,QUdpSocket不支持主机名而只支持主机地址。
将主机名转换为IP地址,有两种选择,一是使用静态函数QHostInfo::fromName(),该函数是阻塞的;二是使用静态函数QHostInfo::lookupHost(),该函数是非阻塞的。
QUdpSocket::bind()
readyRead() signal
QUdpSocket将收到的datagram排队,并容许客户一次访问一个datagram。
Chapter 15 XML
Qt中的QtXml模块提供了两组不一样的API用于读取XML文档
SAX(Simple API for XML):经过virtual function直接向应用程序报告"parsing event“。
DOM(Document Object Model):将XML文档转换为树型结构。
SAX接近底层,速度更快;DOM更便于使用。
15.1 Readin XML with SAX
SAX是事实上的读取XML文档的标准API,Qt中的SAX类仿照了Java中的SAX2的实现。
Qt提供了一个SAX-based的non-validateing类型的XML解析器QXmlSimpleReader。解析器在读取文档时,会调用其注册的handler class中的virtual function。
Qt为QXmlSimpleReader提供了若干handler class,对于大多数应用,只须要使用QXmlContentHandlerr和QXmlErrorHandler。
Qt提供了一个对程序员很方便的QXmlDefaultHandler,它对全部的handler class都进行了派生,并对全部virtual function提供了简单的实现。
要使用QtXml库,须要在.pro文件中加入下面一行: "QT +=xml"
15.2 Reading XML with DOM
DOM是由W3C制订的解析XML的标准API,Qt提供了一个non-validating类型的DOM level 2级别的实现,可用于XML文档的读写以及其余操做。
DOM在内存中将XML文件表示为树的形式。
QDomDocument::setContent(),设定要读取的XML文档。
15.3 Writing XML
Qt中存在两种生成XML文档的方法
1). 构建一个DOM tree,并对其调用save()
2). 手动输出XML格式
两种方式之间的选择独立于读取XML文档时选择SAX或是DOM
默认状况下,QDomDocument::save()生成文档时采用UTF-8编码格式。
Chapter 16 Providing Help
QWidget::setToolTip()用于为Widget设置相应的tip文本。 一样,QAction::setToolTip()为Action设置相应的tip文本;若没有显式的为Action设置tip文本,Action会自动的使用action text。
setStatusTip(),该函数为Widget和Action添加 status tip。
QTextBrowser类能解析大量HTML标签,可用于显示基于HTML的文本内容
Qt Assistant支持索引和文本搜索功能,能够很好的用于提供在线帮助
要使用Qt Assistant,必须在程序中书写必要的代码让Qt Assistant能察觉到文档的所在。
Qt程序与Qt Assisant之间的通信是由QAssistantClient这个类来负责的;该类属于一个单独的类库,要使用该类库,须要在.pro中添加下面一行: CONFIG +=assistant
QAssitantClient类的构造函数以文件路径做为首个参数,用于肯定 Qt Assistant可执行文件的位置。
Chapter 17 Internationalization
Qt4为国际化内置了不少良好的支持:
1). Qt的API接口和内部实现均是基于Unicode的
2). Qt的文本引擎支持全部的non-Latin的书写系统,包括阿拉伯、中日韩、希伯来、印度、泰国等。
3). Qt的layout 引擎为right-to-left风格的阿拉伯文和希伯来文提供了相应的支持。
4). 某些语言在输入文本时须要使用专用的输入法,QLineEdit和QTextEdit能够和系统中安装的任意输入法协调工做。
Qt提供了用于文本翻译的GUI工具:Qt Linguist,以及两个辅助命令行程序 lupdate和lrelease。
大多数程序都是在启动阶段就根据用户的locale setting加载合适的translation file,然而某些状况下用户要求可以在运行时实时的切换界面语言。
17.1 Working With Unicode
QString以Unicode来存储字符串,QString中的每一个字符都是一个16-bit的QChar而不是8-bit的char。
对QString中的某个位置进行赋值操做,能够经过字符方式来肯定新值,也能够经过数值方式来肯定,例如要将类型为QString的str的首字符设为'A',能够有下面两种方式:
str[0]='A';
str[0]=QChar(0x41);
基于QChar之上的编程不一样于基于char。要得到一个QChar变量的编码值,对其调用unicode()函数;要得到一个QChar变量对应的 ASCII或Latin-1编码值,对其调用toLatin1(),若原来QChar中存放的是non-latin字符,该函数返回'\0'。
Qt为QChar类提供了基于Unicode的判断函数,如isPrint(),isSpace(),isLetter(),isNumber()等,其工做与C++标准库提供的isalpha(),isdigit(),isspace()相似。
Qt负责将Unicode编码的QString正常显示,并在须要和其余系统进行通信时转换为相关的编码格式。
默认状况下QTextStream使用系统本地的8-bit编码格式(可经过调用QTextCodec::codecForLocale()查看)读写文本文件,对于美国和西欧,这一般意味着使用Latin-1。
能够调用QTextStream::setCodec()来自定义读写文件时所使用的编码,如 stream.setCodec("UTF-16");
UTF-16格式与QString的内存表示一致,所以使用UTF-16读写Unicode字符串速率会很高,缺点是在存储纯ASCII数据时会有较大的开销。
setCodec()的参数是一个合适的QTextCodec对象,由其负责完成Unicode和本地编码之间的转换。
QTextCodec::codecForName():根据参数中给出的编码名称返回对应的QTextCodec对象。
在读文本文件时,默认状况下QTextStream可以自动检测Unicode编码(依据是0xFFFE,这个Unicode byte order mark);能够经过调用setAutoDetectUnicode(false)来关闭该特性。
默认状况下,Qt将传递给函数tr()的参数视为Latin-1编码的字符串;能够调用静态函数QTextCodec::setCodecForTr()来改变该默认设置,而自定义编码方式。注意,这必须在第一次调用tr()以前就完成。
然而即使如此,代码中的其它显式字符串仍会被解释为Latin-1字符串;解决方法之一是利用QTextCodec对象的toUnicode()函数,也能够调用QText::setCodecForCStrings()来告知Qt在const char*与QString之间进行转换时采用何种编码。
17.2 Making Applications Translation-Aware
要实现程序的多语言化,须要完成两件事情:
1). 确保程序中每一个用户可见的字符串都被tr()处理。
2). 确保程序启动时加载translation file(.qm)。
tr()是在QObject中定义的静态函数,而且在每一个使用了Q_OBJECT宏的子类都被overridden。
tr()返回字符串的一个翻译版本,若是存在的话;不然将输入参数原样返回。
要准备transaltion file,须要使用Qt提供的lupdate,该工具将代码中全部出如今tr()中的可见字符串提取出来并生成待翻译的translation file,这样的translation将发送给翻译者来完成翻译工做。
调用tr()函数的通常形式为
Context::tr(sourceText,comment);
其中Context是tr()所属的类,comment是可选参数,用于为翻译者提供附加信息。
当在一个全局函数中调用tr()时,必须显式的指明相应的Context(类)。
QApplication::translate() 函数完成与tr()函数相同的工做。
tr()和QApplication::translate()完成双重工做:一方面供lupdate从中提取用户可见字符串,另外一方面从translation file中提取字符串的对应翻译。
尽管对一个字符串变量而不是字符串常值调用tr()并非个好主意,然而实际上也是能够做到的,这须要在将字符串常值赋值给某个字符串变量时调用QT_TR_NOOP()宏,该宏不进行任何操做,只是为lupdate提供标识。宏QT_TRANSLATE_NOOP完成一样的工做,不一样之处在于参数中能够指定context,这对于初始化类外变量颇有用。
如何能确保程序员在编写代码时将全部用户可见字符串用tr()包裹起来而不出现遗漏呢?能够经过在包含任何Qt头文件以前定义预约义符号 QT_NO_CAST_FROM_ASCII这个来告诉Qt禁止从const chat * 到QString的自动转换;一般在.pro文件中添加下面一行:
“DEFINES +=QT_NO_CAST_FROM_ASCII"来实现这必定义。
这样就强迫每一个字符串常量在使用时必须被tr()或QLatin1String()函数包裹从而转换为QString,这取决于该字符串常量是否须要被翻译。
静态函数QLocale::system()返回一个QLocale对象,以提供用户的locale信息。
QTranslator对象只能一次加载一个translation file;经过使用多个QTranslator对象,Qt程序能够安装任意数量的translator;
QApplication在寻找合适的翻译时会使用全部已安装的QTranslator。
QTranslator::load()用于加载translation file (.qm)
QApplication::setLayoutDirection(),该函数能够改变文本的书写方向。
QLocale类提供本地化的数字和日期/时间格式。
17.3 Dynamic Language Switching
当Qt检测到环境变量中的locale设置发生变化时,会建立一个LocalChange event;若要处理该event,须要从新实现QWidget::changeEvent()。
当QApplication上已安装的QTranslator的内容发生变化时,Qt会建立一个LanguageChange event。
不要将LocalChange和LanguageChange两个event混淆;前者的发生是由系统变化致使的,所通知的对象是Qt应用程序;后者的产生是Qt自身致使的,所通知的对象是程序的widgets。
17.4 Translating Applications
翻译的三个步骤:
1). 开发人员运行lupdate程序从源代码中提取用户可见字符串,并建立XML格式的.ts文件
2). 翻译人员运行Qt linguist这个GUI工具,完成文本的翻译工做
3). 开发人员运行lrealease这个工具,生成应用程序能够经过QTranslator加载的二进制.qm文件
lrelease的工做就是将可读文本的.ts文件转换为二进制的.qm文件。
lupdate默认状况下假设全部tr()中的字符串都采用Latin-1编码;若实际状况并不是如此,须要在.pro文件中添加CODECFORTR这一项;该工做和在程序中调用QTextCodec::setCodecForTr()两者是缺一不可的。
Chapter 18 Multithreading
18.1 Creaing Threads
Qt中提供多线程的机制很简单:建立QThread的派生类,并从新实现其保护成员函数run()。
QThread::run(),被调用来开始线程的执行,在run()结束时线程终止。
QThread::terminate(),用来终止线程的执行,非阻塞操做,并不保证线程的当即终止;能够在调用QThread::terminate()以后调用QThread::wait()来实现同步等待。
terminate()并非值得推荐结束线程的方法,由于它强制线程终止而不给线程任何清场的机会。
18.2 Synchronizing Threads
Qt中用于线程同步的类包括QMutex,QReadWriteLock,QSemaphore和QWaitCondition
QMutex
QMutex::lock() 阻塞操做
QMutex::trylock() 非阻塞操做
QMutex::unlock()
QMutexLocker是Qt提供的用于简化Mutex操做的一个类——QMutexLocker的构造函数以一个QMutex对象为参数,并对其自动执行lock操做;而在析构函数则对其自动执行unlock操做。
QReadWriteLock能够容许同时进行多个读操做或一个写操做。
QReadWriteLock::lockForRead()
QReadWriteLock::lockForWrite()
QReadWriteLock::unlock()
QSemaphore是对Mutex的扩展;与读写锁不一样的是,信号量能够用来保护一批相同的资源,而不仅是一个。
QSemaphore::acquire(int n=1)
QSemaphore::release(int n=1)
QSemaphore::available()
QWaitCondition和QMutex联合使用,能够容许一个线程在某个条件知足时唤醒其余线程,比起单独使用QMutex能实现更精确的控制。
QWaitCondition::wait()的参数是一个状态为locked的QMutex,该函数在阻塞本线程前会将这个QMutex解锁,并在函数返回前对其lock。
TLS(thread-local storage)
较好的实现方法是使用QThreadStorage<T>类,该类经常使用来实现cache,这样能够避免使用mutex时lock,unlock以及等待带来的开销。
因为某些编译器的问题,QThreadStorage<T>中只能存放指针。
QThreadStorage::hasLocalData()
QThreadStorage::setLocalData()
18.3 Communicating with MainThread
当Qt程序运行时,主线程是惟一的线程,而且是惟一容许建立QApplication或QCoreApplication对象并对其调用exec()的线程。在调用exec()以后,主线程要么是在等待event的发生,要么是在处理一个event。
主线程能够经过建立QThread的子类来开始新线程。
以前介绍的mutex,read/write lock,semaphore等都可用于新线程之间的通信,可是却不能用于和主线程的通信,由于这会致使主循环的event loop被阻塞并"冻结"UI。
解决方案是在主线程与新线程之间跨线程的使用signal-slot机制。
一般状况下signal-slot机制是同步工做的,这意味着当signal被emit时,与之想联系的slot会被当即调用。
然而,当该机制用于将不一样线程中的object链接起来时,则变为异步机制。这样的链接是在底层是经过建立并传递event来实现的;slot被signal的接收对象所在的线程的event loop所调用。
默认状况下,一个QObject对象存在于其被建立的线程之中;这能够在任什么时候候调用QObject::moveToThread()被改变。
18.4 Using Qt's Classess in Secondary Threads
Thread-safe & Reentrant 注意留意这两个概念应用在函数和类之上的不一样。
对于类,若是它的全部成员函数均可以被不一样线程同时调用而不相互影响——即便这些调用是针对同一个类对象,那么该类被定义为thread-safe。
对于类,若是其不一样实例能够在不一样线程中被同时使用而不相互影响,那么该类被定义为reentrant;然而,不一样线程中同时访问同一个reentrant类对象,并非安全的,这样的访问须要用mutex进行保护。
在Qt的定义中,在类这个层次,thread-safe是比reentrant更严格的要求,这和在函数层次上的关系正好相反。
一般状况下C++的类只要不使用全局或其它共享变量,就是reentrant的。
Qt中大多数non-GUI的类,是属于reentrant的。QObject是reentrant的,可是须要注意如下几点:
1). 子QObject必须在父QObject所属的线程中被建立,这意味着在非主线程中的对象在建立时不容许以QThread做为parent,由于后者是在主线程或另一个非主线程中被建立的。
2). 在删除一个QThread对象前,必须将对应线程中建立的全部对象都销毁。
3). 对象必须在其被建立的线程中被删除。
若是须要删除存在于另外一个线程中的对象,必须调用线程安全的QObject::deleteLater()函数,该函数会发送一个"defered delete" event。
QWidget及其子类不是reentrant的。
Chapter 19 Creating Plugins
Qt提供了Qlibrary类,用于以一种平台无关的方式实如今程序运行时加载共享库。
19.1 Extending Qt with Plugins
Qt自己能够被不少类型的plugin扩展,最多见的包括database drivers,image formats,text codecs等。
对于每种类型的plugin,一般都须要两个类:一个wrapper类实现该类通用的plugin API,以及一个或多个handler类,每一个类实现一个plugin特定的API。
在plugin的.cpp文件中,须要使用Q_EXPORT_PLUGIN()这个宏来确保plugin可以被Qt识别。
plugin真正执行的操做都是经过其handler类来实现的。
plugin的.pro文件与应用程序不一样。默认状况下.pro文件使用app模板,然而这里必须使用lib模板,由于plugin属于库,而不是一个独立的应用程序。
QCoreApplication::addLibraryPath( ),为程序添加新的库路径。
19.2 Making Application Plugin-Aware
应用程序的plugin实际是实现了一个或多个接口(interface)的动态库。应用程序与plugin之间的通信是经过interface的virtual table来完成的。
一个接口(interface)一般声明一个virtual析构函数,一个返回QStringList的virtual函数,以及一个或多个其余virtual函数。
在接口声明的尾部,须要调用Q_DECLARE_INTREFACE2()来将该interface与某个标识符关联起来。
QPluginLoader类用于在运行时加载plugin。
QPluginLoader::load(),一般不须要显式调用,由于instance()函数会在必要时调用该函数完成加载。
QPluginLoader::instance(),返回一个指向plugin对象的QObject *指针。
同一个插件plugin能够成功cast至多个interface,由于plugin能够经过多重继承来提供多个interface。
19.3 Writing Application Plugins
应用程序的plugin是其要提供的interface和QObject两者的子类。
在plugin的源代码中,须要为其提供的每一个Interface都要使用Q_INTERFACES()宏,来保证moc和qobject_cast<T>之间的协调工做。
在.cpp文件的尾部,一样须要调用Q_EXPORT_PLUGIN2()宏来使该plugin对于Qt可用。
Chapter 20 Platform-Specific Features
20.1 Interfacing with Native APIs
在每一个平台上,Qt都为QWidget提供了一个winId()函数,返回window ID或是句柄;QWidget还提供了一个静态函数find(),返回一个特定window ID对应的widget。咱们能够将得到的window ID传递给Native API来执行平台特定的操做。
Qt定义了如下系统标志:Q_WS_WIN,Q_WS_X11,Q_WS_MAC,Q_WS_QWS(Qtopia)。
QSysInfo::WindowsVersion QSysInfo::MacintoshVersion 这两个静态变量存储着WIN和MAC操做系统的版本信息
20.2 Using ActiveX on Windows
ActiveX构建于Microst COM之上,它为使用组件的应用程序定义了一套接口,为提供组件的库和应用程序定义了另外一套接口。
ActiveQt由两个模块组成:
QAxContainer模块容许用户使用COM object并在Qt程序中内嵌ActiveX控件。
QAxServer模块容许用户导出自定义的COM object以及用Qt编写的ActiveX控件。
Q_ENUMS()宏的做用是告知moc其"宏参数"是枚举类型。
QAxContainer模块由三个类组成:QAXObject封装一个COM object,QAxWidget封装一个ActiveX控件,QAxBase为QAxObject和QAxWidget实现核心COM功能。
QAxObject派生自QAxBase和QObject,QAxWidget派生自QAxBase和QWidget。
COM中的数据类型会被自动转换为合适的Qt数据类型。
QAxBase::setControl()
QObject::setProperty()可用于设置COM property和Qt property。
要连接QAxContainer库的话,须要在.pro文件中添加下列一行:"CONFIG +=qaxcontainer"
QAxBase::dynamicCall()
注意,QAxObject和QAxWidget的子类没法定义新的property,signal和slot。
QAxServer模块容许将一个标准Qt程序转换为一个ActiveX server。该server能够是共享库,也能够是独立的应用程序。共享库形式的server被称为in-process servers,而独立应用程序形式的server被称为out-of-process server。
QAxBindable在widget与ActiveX client之间提供了一个接口。
在Qt中处理多重继承中,若是基类中存在QObject的派生类,必须将这样的类放在首位。
QAXFACTORY_DEFAULT()宏的做用是导出一个AxtiveX控件,能够用于仅导出一个控件的ActiveX server;当server要导出多个控件时,不能使用QAXFACTORY_DEFAULT()宏。
QApplication可以识别命令行中的-activex参数,并使应用程序做为server而运行。
Q_CLASSINFO()宏
20.3 Handling X11 Session Management
为了使一个Qt/X11应用程序意识到session manager的存在,须要从新实现QApplication::saveState()函数,并在该函数中保存应用程序的状态信息。
当用户启动shutdown操做时,程序员能够经过从新实现QApplication::commitData()来获取控制权,这容许应用程序保存未保存的数据,而且与用户交互——若是可能的话;这部分session management在X11和Windows上都被支持。
QObject::setObjectName()
void QApplicatoin::saveState(QSessionManager &)——该函数在session manager但愿应用程序保存其状态时被调用,QSessionManager类型的参数容许应用程序与session manager进行通信。
discard command:是指session manager必须执行的用删除任何存储当前状态信息的命令。
restart command:是指session manager必须执行的用以从新启动应用程序的命令。
QSessionManager::setDiscardCommand(QStringList &)
QSessionManager::setDiscardCommand(QStringList &)
默认状况下,Qt提供的restart command的格式为: appname -session id_key
QSessionManager::release()
QSessionManager::cancel()
QApplication:isSessionRestored()