1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
QSplashScreen *splash = new QSplashScreen;
splash->setPixmap(QPixmap( ":/images/splash.png" ));
splash->show();
Qt::Alignment topRight = Qt::AlignRight | Qt::AlignTop;
splash->showMessage(QObject::tr( "Setting up the main window..." ),
topRight, Qt::white);
... ...
splash->showMessage(QObject::tr( "Loading modules..." ),
topRight, Qt::white);
... ...
splash->showMessage(QObject::tr( "Establishing connections..." ),
topRight, Qt::white);
... ...
splash->finish(&mainWin);
delete splash;
return app.exec();
Chapter 4 Implementing Application Functionality
4.1 The Central Widget
- QMainWindow的中心区域可以是任意类型的widget.
- 使用标准的Qt Widget
- 使用自定义widget
- 使用带layout管理器的简单QWidget
- 使用splitter(分割器) --- QSplitter
- 使用MDI区域 --- QMdiArea widget
- 本例使用QTabelView的派生类用于中间widget. 其支持了许多spreadsheet操作, 但不支持剪贴板操作, 不理解spreadsheet公式
4.2 Subclassing QTableWidget
- QTabelWidget为一有效的网格来显示两维稀疏数组. 随着用户的滚动显示需要的网格. 当用户输入内容时, 会自动创建一个QTabelWidgetItem保存文本.
- 另外一个更多功能的table是 QicsTable, 见 http://www.ics.com/
- QTabelWidgetItem 不是widget, 为纯数据类, Cell是其派生类.
- 一般而言, QTabelWidget在一空白单元格输入文本时, 会自动创建一QTabelWidgetItem来保存该文本.
- 在本spreadsheet中, 我们希望创建Cell对象来保存文本, 则通过在构造函数中调用setItemPrototype来实现,
- 在内部, 每当需创建一个新的条目时, QTabelWidget就会克隆被当作prototype的条目
- setSelectionMode(ContiguousSelection); // 允许矩形选择内容
- setHorizontalHeaderItem(i, item); // 设置第一行的头内容
- 将数据存储为条目的方式在QListWidget和QTreeWidget中也运用到, 操作为QListWidgetItems 和 QTreeWidgetItem
- item类可以作为数据控制器, 如QTableWidgetItem则可以保存一些属性, 如字符串, 字体, 颜色和图标, 以及返回QTableWidget的指针. 也保存QVariant数据
- Qt中保存自定义数据的做法是使用 setData()和QVariant实现, 如果需要void指针, 则通过派生item类, 并增加void指针成员变量
- QTableWidget::setItem 设置条目
4.3 Loading and Saving
- 使用QFile和QDataStream实现文件操作, 跨平台的二进制I/O操作
?
1
2
3
4
5
6
7
8
9
10
11
|
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly)) {
... ...
}
QDataStream out(&file);
out.setVersion(QDataStream::Qt_4_3);
out << quint32(MagicNumber);
QApplication::setOverrideCursor(Qt::WaitCursor);
out << .......
QApplication::restoreOverrideCursor();
|
- 整数类型: qint8, quint8, qint16, quint16, qint32, quint32, qint64, quint64
- QDataStream 使用二进制近期的大多数版本, 可以明确指定QDataStream的版本, 解决读取的兼容问题
- QDataStream不仅可以用于QFile, 也可用于QBuffer, QProcess, QTopSocket, QUdpSocket, QSalSocket
- QTextStream可以用来读取写入文本文件
4.3 Implementing the Edit Menu
- QApplication::clipboard()->setText(str); // 设置剪贴板内容
4.5 Implementing the Other Menus
4.6 Subclassing QTableWidgetItema
- QTableWidgetItem可以保存数据, 每个数据都有两个值, 一个是编辑的值, 一个是显示的值, 大部分时候相同, 但是当其为公式的时候可能不同
- Cell没有提供text()函数,由QTableWidgetItem解决, 等同于调用 data(Qt::DisplayRole).toString();
- value() 函数
- 如果有 单引号"'", 则表示为字符串
- 如有"=", 则计算
- 否则直接转换为double
- 如果都不是就直接输出字符串
Chapter 5: Creating Custom Widgets
- 创建自定义widget的方法: 现有Qt Widget派生或者直接从QWidget派生生成
5.1 Customizing Qt Widgets
- 本章节例子: 让SpinBox实现十六进制数的显示和使用
- 类HexSpinBox继承自类QSpinBox, 构造函数, 重写基类函数validate, valueFromText, textFromValue, 增加私有成员QRegExpValidator *validator;
- 输入文本的验证
- 可能的三个返回值: Invalid, Intermediate, Acceptable
?
1
2
3
4
|
QValidator::State HexSpinBox::validate(QString &text, int &pos) const
{
return validator->validate(text, pos);
}
|
- QSpinBox在用户按下spin box的向上和向下箭头时, 调用该函数更新编辑区部分
?
1
2
3
4
|
QString HexSpinBox::textFromValue( int value) const
{
return QString::number(value, 16).toUpper();
}
|
- 当用户在编辑区输入一个值并回车的时候, 调用该函数. 执行字符串到值的转换
?
1
2
3
4
5
|
int HexSpinBox::valueFromText( const QString &text) const
{
bool ok;
return text.toInt(&ok, 16);
}
|
- 实现自定义widget的步骤
- 选择合适的Qt Widget
- 派生类
- 重写部分虚函数改变其行为.
- 如果我们仅仅想改变一个widget的外观, 我们可以应用一个style 表单或者实现一个自定义style, 而不是派生该widget类.
5.2 Subclassing QWidget
- 在Qt Designer中设计自定义widget
- 使用"Widget"创建一个新的form
- 增加需要的widget至该form, 并布局这些widget
- 设置signals和slots的连接
- 如果signals和slots还不能实现所有的行为, 则从QWidget和该uic生成类派生一个新类, 完成必要的代码实现这些行为
- 当然, 也可使用纯代码实现自定义widget, 不管是什么方法, 都必须从QWidget派生该类.
- 从QWidget派生类, 并重实现一些事件处理函数来绘制该widget, 对鼠标点击做出反应.
- 这个方法可以让我们完全自由的定义和控制该widget的外观和行为.
- 代码中使用 Q_PROPERTY() 宏声明自定义属性. 每个属性有一数据类型, 读取函数, 可选的写入函数
- 在Qt Designer中, 可以在 property editor 中自定义属性.
- QRgb 为32位无符号整数, 可使用qRgba和qRgb来返回该值.
- Qt 提供了两个类型保存颜色: QRgb和QColor, QRgb只用于QImage, 用来保存32位像素数据. QColor用于许多有用的函数保存颜色.除了QImage.
- size policy用来告知layout 系统是否可以拉伸和收缩.
- QSizePolicy::Minimum 用来告诉layout管理器该widget的size hint是其最小值. 即可以拉伸, 但不能收缩至比该值更小的大小.
- QWidget::updateGeometry() 用于告知包含该widget的layout, 该widget的size hint发生了变化. 而后该layout则会自动适应新的size hint
- 产生paint 事件的几种情况
- 第一次显示该widget
- 该widget的大小发生变化
- 被其他窗口覆盖, 且重新显示被覆盖的地方.
- 我们也可以通过调用 QWidget::update()和QWidget::repaint()函数使得paint事件发生.
- repaint() 需要立即重绘
- update() 则将绘制事件放入处理进程列表.
- 每个widget都有一个调色板用于指定使用哪个颜色. 缺省设置, 一个widget的调色板都是采用操作系统的颜色方案.
- 一个widget的调色板由三个颜色组组成: 活跃的, 非活跃的, 不能够使用的.
- 活跃组用于当前活跃窗口
- 非活跃组用于其他窗口
- disable使用组用于任意窗口的disable widget
- QWidget::palette() 返回该widget的调色板 QPalette对象, 颜色组则为特定的枚举类型 QPalette::ColorGroup.
- 调色板则可以根据不同的role得到其笔刷brush, 如QPalette::foreground()
- mouseMoveEvent 事件在按下鼠标的时候移动鼠标才产生, 除非你调用了QWidget::setMouseTracking() 函数
- Qt::WA_StaticContents 属性, 该属性用于告知Qt该widget的内容在大小变化时不需要改变, 内容始终对应在widget的左上角.
- 每当大小变化是, paint 事件只对原来未显示的内容起作用.
5.3 Integrating Custom Widgets with Qt Designer
- 在Qt Designer中使用自定义widget, 有两种方法:
- 在Widget上直接提升自定义widget的方法
- 创建基Widget在Qt Designer上
- 右键该widget, 选择 Promote to Custom Widget
- 填充弹出对话框的类名和头文件名称
- 而后该uic生成的代码会包含上面填充的头文件, 而非基Widget的头文件. 而在Qt Designer, 显示的还是基Widget, 修改基Widget的属性.
- 缺点:
- 不能够在Qt Designer修改该自定义widget的自定义属性
- 不能够显示该Widget, 而是显示其基Widget
- 插件方式, 则生成一插件库让Qt Designer在运行期间动态加载
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
#include
class IconEditorPlugin : public QObject,
public QDesignerCustomWidgetInterface
{
Q_OBJECT
Q_INTERFACES(QDesignerCustomWidgetInterface)
public :
IconEditorPlugin(QObject *parent = 0);
QString name() const ;
QString includeFile() const ;
QString group() const ;
QIcon icon() const ;
QString toolTip() const ;
QString whatsThis() const ;
bool isContainer() const ;
QWidget *createWidget(QWidget *parent);
};
IconEditorPlugin::IconEditorPlugin(QObject *parent)
: QObject(parent)
{
}
// 返回插件的名称
QString IconEditorPlugin::name() const
{
return "IconEditor" ;
}
// 该函数用于返回自定义widget的头文件名称. 会在uic工具生成的代码中包含该头文件
QString IconEditorPlugin::includeFile() const
{
return "iconeditor.h" ;
}
// 用于Qt Designer的widget分组
QString IconEditorPlugin::group() const
{
return tr( "Image Manipulation Widgets" );
}
// 返回在Qt Designer用于表示该自定义widget的图标
QIcon IconEditorPlugin::icon() const
{
return QIcon( ":/images/iconeditor.png" );
}
QString IconEditorPlugin::toolTip() const
{
return tr( "An icon editor widget" );
}
// 用于"What's This?"
QString IconEditorPlugin::whatsThis() const
{
return tr( "This widget is presented in Chapter 5 of C++ GUI "
"Programming with Qt 4 as an example of a custom Qt "
"widget." );
}
// 该自定义widget是否为容器
bool IconEditorPlugin::isContainer() const
{
return false ;
}
// Qt Designer 通过该函数创建该widget的实例
QWidget *IconEditorPlugin::createWidget(QWidget *parent)
{
return new IconEditor(parent);
}
// 使用该宏来使得其该插件可用于Qt Designer
// 第一个参数为插件的名称, 第二个则为实现的类
Q_EXPORT_PLUGIN2(iconeditorplugin, IconEditorPlugin)
|
- 工程文件:
?
1
2
3
4
5
6
7
8
9
|
TEMPLATE = lib
CONFIG += designer plugin release
HEADERS = ../iconeditor/iconeditor.h \
iconeditorplugin.h
SOURCES = ../iconeditor/iconeditor.cpp \
iconeditorplugin.cpp
RESOURCES = iconeditorplugin.qrc
DESTDIR = $$[QT_INSTALL_PLUGINS]/designer
// QT_INSTALL_PLUGINS --- Qt的插件安装目录
|
- 可以使用QDesignerCustomWidgetCollectionInterface将多个自定义widget集成至一个plugin中
5.4 Double Buffering
- 双缓存是GUI程序的一个技术, 有两个部分组成: 在屏幕背后渲染一个widget的像素映射内容, 而后将该像素映射拷贝至屏幕. 这个技术可以避免屏幕出现闪烁.
- 当一个widget的渲染很复杂以及需要重复绘制时, 可以使用双缓存技术.
- 长时间保存像素映射(pixmap), 随时准备下一个paint事件, 当接收到paint事件时, 拷贝该pixmap至widget
- QPointF 是一个浮点版本的QPoint
- QPixmap类型变量, 可表示绘制到widget的pixmap. 先在屏幕后将像素写到pixmap, 然后拷贝该pixmap至屏幕的widget
- QWidget::setBackgroundRole() 设置填充背景的方式, 本例使用调色板的"dark"成分来替换"window"成分. 用于填充任何新的增加窗口的像素, 在paint事件之前.
- 注意还需要调用 setAutoFillBackground(true); 来启动该机制.
- 缺省情况, 子widget都是继承父widget的背景
- QSizePolicy::Expanding 表示widget可以拉伸收缩. QSizePolicy::Preferred 表示widget尽量选择size hint的大小, 可以收缩至 size hint的最小值, 拉伸至无限大小
- QtWidget::setFocusPolicy(Qt::StrongFocus) 表示该widget通过鼠标点击或者键击tab接受焦点.
- 本例接受焦点后, 该widget会接受键盘事件, 本例+表示放大, -表示缩小. 向上表示向上滚动等等
- QtWidget::adjustSize() 表示设置为该widget的size hint的大小
- 本例 setPlotSetting()表示设置一个PlotSetting, 每当放大一次该plot, 则调用PlotSetting构造函数构建一个新的缩放.
- 调用refreshPixmap() 用于更新显示内容. 而不是使用update(), 先更新QPixmap至最新, 而后调用update()拷贝pixmap至widget
- 当widget具有焦点时, 焦点矩形的绘制:
- painter调用drawPrimitive()方法, 第一参数为QStyle::PE_FrameFocusRect, 第二参数为QStyleOptionFocusRect对象: painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);
- 第二个参数调用initForm初始化, 该函数的参数为widget
?
1
2
|
QStyleOptionFocusRect option;
option.initFrom( this );
|
- 设置QStyleOptionFocusRect的背景颜色
?
1 |
option.backgroundColor = palette().dark().color(); |
- 当我们想要使用当前style(风格)绘制时, 我们可以直接调用一个QStyle函数: style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter, this);
- style()返回用于绘制该widget的style, 在Qt中, 一个widget的style都是QStyle的派生类.
- 内置的style包括: QWindowsStyle, QWindowsXPStyle, QWindowsVistaStyle, QMotifStyle, QCDEStyle, QMacStyle, QPlastiqueStyle, QCleanlooksStyle
- 每个style都重新实现了QStyle的虚函数, 通过枚举style在不同的平台上使用正确的style.
- QStylePainter的drawPrimitive()则会调用QStyle内的同名函数绘制"基本元素", 如panels, buttons, forcus rectangle.
- 一般而言, 一个应用程序中的widget style应当与所有的widget的style相同, 但是可以使用QWidget::setStyle()来改变每个widget.
- 通过实现QStyle的派生类, 你可以定义一个自定义style.
- 所有的Qt内置widget都是依赖于QStyle来绘制其本身. 这就是为何这些widget在不同的平台就像其平台自身的widget一样.
- 对于自定义widget, 通常实现QStyle来绘制其自身, 或者使用内置的Qt Widget作为widget来实现与Qt内置widget一样的style>
- 本例的focus rectangle则使用QStyle, 而两个放大缩小按钮则使用Qt 内置widget
- 大小变化则会引发resizeEvent函数的调用
- Qt 提供了两个机制用于控制光标的形状
- QWidget::setCursor() 设置当光标在widget上方时的形状, 如果没有对应的光标, 则使用其父widget的光标设置. 顶层widget缺省使用 箭头光标
- QApplication::setOverrideCursor() 设置在整个应用程序中使用的光标形状, 覆盖各个widget的光标. 直到调用restoreOverrideCursor() 恢复
- QScrollArea 会自动处理鼠标滚轮事件
- QPainter的initFrom()函数用于初始化painter的笔刷, 背景, 字体等参数, 这些参数与给定Widget的设置相同. 由 QWidget 自动调用
Chapter 6 Layout Management
- Qt提供的layout: QHBoxLayout, QVBoxLayout, QGridLayout, QStackedLayout.
- 使用layout的一个理由是使得widget适应字体的变化和程序界面语言的变化.
- 其他可以执行layout管理的类: QSplitter, QScrollArea, QMainWindow, QMdiArea
- QSplitter 会提供以一个splitter条让用户可以拖动或者重置widget的大小.
- QMdiArea则支持MDI(多文档接口)
6.1 Laying Out Widgets on a Form
- 这里管理子widget的layout有三种方式: 绝对位置, 手工layout, layout 管理器
- 绝对位置方式, 通过各个子widget调用setGeometry来设定位置和大小, 主widget则调用setFixedSize设置固定大小
- 使用绝对位置的缺点:
- 用户不能重置其大小
- 一些文本可能由于字体的变化或者语言的变化而被截去部分内容
- 在某些style中, widget可能会有不适当的大小
- 必须手工计算大小和位置, 乏味且容易出错, 维护困难
- 手工layout, 位置仍然绝对, 但是大小可以适应窗口. 通过在 resizeEvent() 方法中实现该功能
- layout 管理器方法, 考虑每个widget的size hint(该size hint依赖于widget的字体, 内容), 同时也考虑最小和最大大小.
- 根据字体的变化, 内容的变化, 窗口大小的变化自动修正大小.
- 三个最重要的layout类是QHBoxLayout, QVBoxLayout, QGridLayout, 这三者都为QLayout的派生类
- 一个layout内的边缘和子widget之间的空格由当前widget的style决定. 也可以使用QLayout::setContentsMargins()和QLayout::setSpacing()来改变
- QGridLayout的语法: layout->addWidget(widget, row, column, rowSpan, columnSpan);
- addStretch() 用来告知layout 管理器填充该处的空白.
- layout 管理器的优点:
- 添加或者移除一个widget, layout会自动修正以适应新的情况, 对hide()和show()也有同样的效果.
- 当子widget的size hint发生改变时, layout 管理器会自动修改以适应新的size hint
- 根据子widget的size hint和最小值来设置layout的最小值
- 有时我们需要修改size policy和widget的size hint来实现我们需要的layout
- size policy的值:
- Fixed --- 固定的layout, 不能拉伸和收缩. 使用size hint的大小
- Minimum --- 表示该widget的最小值就是size hint. 不能够收缩至比该值更小的值. 可以填充可用的空间给widget
- Maximum --- 表示该widget的最大值就是size hint, 该widget可以收缩至其最小值 minimum size hint
- Preferred --- 该widget的preferred值就是size hint, 在需要的时候可以收缩和拉伸
- Expanding --- 表示该widget可以收缩和拉伸, 但其最好选择拉伸
- Expanding 和 Preferred的区别: 当一个form包含两者的widget, 该form大小变化时, 额外的空白处则给予Expanding widget, 而Preferred widget保持为其size hint的大小
- Minimum, Expanding 和 Ignored这两个Size Policy不再经常使用, 后者忽略widget的size hint和最小值hint
- 为了补充水平和垂直部分的size policy, 我们还设定了拉伸因子(strectch factor), 可以设置widget在水平或垂直方向的拉伸
- 如果对一个widget不满意, 我们还可以派生该widget类, 重写其sizeHint()函数
6.2 Stacked Layouts
- QStackedLayout 类对一系列子widget布局, 或者"分页". 且每次只显示一个页面, 隐藏其他页面的内容.
- Qt提供QStackedWidget类表示带内置QStackedLayout的QWidget.
- 页数是从0开始, 设置当前页 setCurrentIndex, 得到一个子widget的页号则使用indexOf().
- QListWidget可以和QStackedLayout配合使用.
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
listWidget = new QListWidget;
listWidget->addItem(tr( "Appearance" ));
listWidget->addItem(tr( "Web Browser" ));
listWidget->addItem(tr( "Mail & News" ));
listWidget->addItem(tr( "Advanced" ));
stackedLayout = new QStackedLayout;
stackedLayout->addWidget(appearancePage);
stackedLayout->addWidget(webBrowserPage);
stackedLayout->addWidget(mailAndNewsPage);
stackedLayout->addWidget(advancedPage);
connect(listWidget, SIGNAL(currentRowChanged( int )),
stackedLayout, SLOT(setCurrentIndex( int )));
...
listWidget->setCurrentRow(0);
|
- currentRowChanged(int)信号发送给setCurrentIndex(int) slot 实现页面切换
- setCurrentRow() 设置当前页面
- Qt Designer实现分页
- 用"dialog"模板或者"widget"模板创建新的form
- 添加QListWidget和QStackedWidget
- 填充每个页面的widget和layout
- 水平方向布局这两个widget
- signal和slot连接 currentRowChanged(int) --> setCurrentIndex(int)
- 设置list widget的currentRow属性
6.3 Splitters
- QSplitter的子widget根据创建的顺序自动排列在一起. 相邻的widget之间有splitter bar. 下面是创建的代码
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
int main( int argc, char *argv[])
{
QApplication app(argc, argv);
QTextEdit *editor1 = new QTextEdit;
QTextEdit *editor2 = new QTextEdit;
QTextEdit *editor3 = new QTextEdit;
QSplitter splitter(Qt::Horizontal);
splitter.addWidget(editor1);
splitter.addWidget(editor2);
splitter.addWidget(editor3);
...
splitter.show();
return app.exec();
}
|
- QSplitter 派生自QWidget, 像其他的widget一样使用.
- MailClient的例子
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
MailClient::MailClient()
{
...
rightSplitter = new QSplitter(Qt::Vertical);
rightSplitter->addWidget(messagesTreeWidget);
rightSplitter->addWidget(textEdit);
rightSplitter->setStretchFactor(1, 1);
mainSplitter = new QSplitter(Qt::Horizontal);
mainSplitter->addWidget(foldersTreeWidget);
mainSplitter->addWidget(rightSplitter);
mainSplitter->setStretchFactor(1, 1);
setCentralWidget(mainSplitter);
setWindowTitle(tr( "Mail Client" ));
readSettings();
}
|
- setStretchFactor 设置拉伸因子, 缺省是随着大小变化, 各部分的比例不变, 第一个参数是以第一个widget为0的索引值, 第二个参数设置拉伸因子, 缺省为0
- 保存设置
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
QSettings settings( "Software Inc." , "Mail Client" );
settings.beginGroup( "mainWindow" );
settings.setValue( "geometry" , saveGeometry());
settings.setValue( "mainSplitter" , mainSplitter->saveState());
settings.setValue( "rightSplitter" , rightSplitter->saveState());
settings.endGroup();
// 读取设置
QSettings settings( "Software Inc." , "Mail Client" );
settings.beginGroup( "mainWindow" );
restoreGeometry(settings.value( "geometry" ).toByteArray());
mainSplitter->restoreState(
settings.value( "mainSplitter" ).toByteArray());
rightSplitter->restoreState(
settings.value( "rightSplitter" ).toByteArray());
settings.endGroup();
|
6.4 Scrolling Areas
- 如果需要使用滚动条, 最好使用QScrollArea而不是自己实现QScrollBar和滚动功能, 因为这样太复杂
- 使用QScrollArea的方法是调用setWidget使得该widget成为QScrolllArea视口的子类. 访问视口, QScrollArea::viewport()
?
1
2
3
4
5
|
QScrollArea scrollArea;
scrollArea.setWidget(iconEditor);
scrollArea.viewport()->setBackgroundRole(QPalette::Dark);
scrollArea.viewport()->setAutoFillBackground( true );
scrollArea.setWindowTitle(QObject::tr( "Icon Editor" ));
|
- 通过调用setWidgetResizable(true)告知该QScrollArea可以自动的重置widget的大小. 这样可以使用其size hint之外的空间
- 缺省情况是当视口比widget更小的时候才显示滚动条, 如果想滚动条永远显示, 则使用以下代码:
?
1
2
|
scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
- QScrollArea派生自QAbstractScrollArea, QTextEdit和QAbstractItemView的基类为QAbstractScrollBar.
6.5 Dock Windows and Toolbars
- Dock Window表示那些可以在QMainWindow Dock的窗口以及可以独立出来的窗口.
- QMainWindow 提供了四个浮动区域, 上,下, 左, 右.
- 每个dock window都有其标题条, 可通过QDockWidget::setFeatures() 设置其属性
- QMainWindow::setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea); // 上面的函数设置左上角区域属于左边的dock widget区域
- 如何在一个QDockWidget包装一已存widget, 且插入右边的dock区域
?
1
2
3
4
5
6
|
QDockWidget *shapesDockWidget = new QDockWidget(tr( "Shapes" ));
shapesDockWidget->setObjectName( "shapesDockWidget" );
shapesDockWidget->setWidget(treeWidget);
shapesDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea
| Qt::RightDockWidgetArea);
addDockWidget(Qt::RightDockWidgetArea, shapesDockWidget);
|
- 每个对象都有一个对象名, 在调试的时候有用
- Dock widget和Toolbar都需设置对象名, 这样可使用函数QMainWindow::saveState()和QMainWindow::restoreState() 保存和恢复状态和位置大小
- 工具条
?
1
2
3
4
5
6
7
8
9
10
|
QToolBar *fontToolBar = new QToolBar(tr( "Font" ));
fontToolBar->setObjectName( "fontToolBar" );
fontToolBar->addWidget(familyComboBox);
fontToolBar->addWidget(sizeSpinBox);
fontToolBar->addAction(boldAction);
fontToolBar->addAction(italicAction);
fontToolBar->addAction(underlineAction);
fontToolBar->setAllowedAreas(Qt::TopToolBarArea
| Qt::BottomToolBarArea);
addToolBar(fontToolBar);
|
- 保存和回复设置
?
1
2
3
4
5
6
7
8
9
10
11
12
|
// 保存
QSettings settings( "Software Inc." , "Icon Editor" );
settings.beginGroup( "mainWindow" );
settings.setValue( "geometry" , saveGeometry());
settings.setValue( "state" , saveState());
settings.endGroup();
// 恢复
QSettings settings( "Software Inc." , "Icon Editor" );
settings.beginGroup( "mainWindow" );
restoreGeometry(settings.value( "geometry" ).toByteArray());
restoreState(settings.value( "state" ).toByteArray());
settings.endGroup();
|
- QMainWindow还给dock window和Toolbar提供了右键菜单
6.6 多文档接口
- 一个MDI应用程序通过使用QMdiArea类作为中心widget以及让每个文档窗口为一个QMdiArea的子窗口
- 子窗口菜单-MDI应用程序提供了一系列菜单选项表示所有的文档窗口, 当前的文档窗口会有选中标志
- 在构造函数中, 创建一个QMdiArea对象并设置为中心widget, 并将subWindowActivated()信号发送给一个slot, 实现菜单的更新
- 在构造函数的结尾部分有一行代码: QTimer::singleShot(0, this, SLOT(loadFiles()));
- 表示0秒的间隔之后调用loadFiles(). 在事件循环为空闲时, 计时器运行完时间, 事实上表示当构造函数完成之后, 主窗口显示之时, 调用loadFiles()函数
- 如果不这样做, 当有大量的文件之时, 直到文件加载完毕之后构造函数还未必完成时, 用户也许会在屏幕上看不到任何东西. 而认为程序失败且重启程序
?
1
2
3
4
5
6
7
8
9
10
11
12
13
|
void MainWindow::loadFiles()
{
QStringList args = QApplication::arguments();
args.removeFirst();
if (!args.isEmpty()) {
foreach (QString arg, args)
openFile(arg);
mdiArea->cascadeSubWindows();
} else {
newFile();
}
mdiArea->activateNextSubWindow();
}
|
- 确保编辑器选择了文本才允许这两个菜单项可以使用
?
1
2
3
4
|
connect(editor, SIGNAL(copyAvailable( bool )),
cutAction, SLOT(setEnabled( bool )));
connect(editor, SIGNAL(copyAvailable( bool )),
copyAction, SLOT(setEnabled( bool )));
|
- QMdiArea的addSubWindow() 函数可以创建一个新的QMdiSubWindow: QMdiSubWindow *subWindow = mdiArea->addSubWindow(editor);
- QActionGroup 确保只有一个窗口菜单选项被选中.
- QMdiArea::activeSubWindow() --- 返回其活跃窗口
- qobject_cast --- 用于强制转换.
- QTextCursor::hasSelection () --- 返回当前文本光标是否选择了文本
- QAction::setChecked() --- 设置选中该Action
- QScintilla --- 代码编辑的widget
- 每个子窗口设置 Qt::WA_DeleteOnClose 属性, 当关闭的时候删除该窗口, 以免内存泄漏
Chapter 7 Event Processing
7.1 Reimplementing Event Handlers
- 在Qt中, 任何事件都是QEvent派生类的实例. Qt 处理上百种事件类型, 通过枚举值来标识出事件类型.
- 举个例子: QEvent::type() 返回 QEvent::MouseButtonPress 则表示一个鼠标按下事件.
- 许多的事件类型都需要存储更多的信息, 例如鼠标按下事件需要知道是哪个按键被按下以及指针所在位置. 这些都保存在QEvent的派生类QMouseEvent中.
- 通过event()函数将事件通知给对象. 该函数从QObject继承而来.
- 在QWidget中实现了大多数通用事件处理函数: mousePressEvent, keyPressEvent, paintEvent.
- 可以创建自定义事件类型并分配给我们自己的事件.
- 键盘事件通过重写keyPressEvent()和keyReleaseEvent()实现.
- Modifier键: Ctrl, Shift, Alt, 可以使用KeyPressEvent() 和 QKeyEvent::modifiers().
- 例如判断 Ctrl + Home
?
1
2
3
4
5
6
7
8
9
10
11
12
|
switch (event->key()) {
case Qt::Key_Home:
if (event->modifiers() & Qt::ControlModifier) {
goToBeginningOfDocument();
}
else
{
goToBeginningOfLine();
}
break ;
... ...
}
|
- 一般而言, Tab和Shift+Tab用于切换widget. 在QWidget::event()中被处理, 该函数在keyPressEvent()之前被调用,
- 如果想要修改该功能, 则重写QWidget::event()函数
?
1
2
3
4
5
6
7
8
9
10
11
|
bool CodeEditor::event(QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast (event);
if (keyEvent->key() == Qt::Key_Tab) {
insertAtCurrentPosition( '\t' );
return true ;
}
}
return QWidget::event(event);
}
|
- 实现快捷键与Action, 处理函数的绑定
?
1
2
3
4
5
6
7
8
9
10
11
|
goToBeginningOfLineAction =
new QAction(tr( "Go to Beginning of Line" ), this );
goToBeginningOfLineAction->setShortcut(tr( "Home" )); // 连接
connect(goToBeginningOfLineAction, SIGNAL(activated()),
editor, SLOT(goToBeginningOfLine()));
goToBeginningOfDocumentAction =
new QAction(tr( "Go to Beginning of Document" ), this );
goToBeginningOfDocumentAction->setShortcut(tr( "Ctrl+Home" ));
connect(goToBeginningOfDocumentAction, SIGNAL(activated()),
editor, SLOT(goToBeginningOfDocument()));
|
- 如果在程序用户界面菜单和工具栏都没有这个Action, 则会使用QShortcut来实现该快捷键功能, 以实现键的绑定
- 可以用QAction::setShortcutContext() 或者 QShortcut::setContext() 修改快捷键的绑定
- 三个事件 timerEvent(), showEvent(), hideEvent()
- updateGeometry() 用于通知widget的layout manager其子widget的size hint可能发生变化, 让其进行修正.
- startTimer() 函数启动一个计时器, 在showEvent()中设置计时器, 可以使得在widget完全显示之后启动计时器
?
1
2
3
4
5
6
7
8
9
10
11
|
void Ticker::timerEvent(QTimerEvent *event)
{
if (event->timerId() == myTimerId) {
++offset;
if (offset >= fontMetrics().width(text()))
offset = 0;
scroll(-1, 0);
} else {
QWidget::timerEvent(event);
}
}
|
- 本例使用 QWidget::scroll() 替换update(), 更有效率, 每次只需要绘制多出的1像素位置的内容.
7.2 Installing Event Filter
- Qt的事件模型一个非常强大的功能就是一个QObject的实例可以监视另一个QObject实例的事件, 在后者QObject的实例看到这个事件之前.
- 通过建立监视器来监控子widget的事件, 来实现特定功能, 使用事件过滤器. 具体有两个步骤:
- 通过在目标上调用installEventFilter()函数来注册目标对象的监视器对象
- 在监视器的eventFilter()函数中处理目标对象的事件
- 一般最好在构造函数中注册监视器对象
?
1
2
3
4
|
firstNameEdit->installEventFilter( this );
lastNameEdit->installEventFilter( this );
cityEdit->installEventFilter( this );
phoneNumberEdit->installEventFilter( this );
|
- 这四个widget首先将发送调用本widget的eventFilter()函数, 而后再到其自身的处理函数
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
bool CustomerInfoDialog::eventFilter(QObject *target, QEvent *event)
{
if (target == firstNameEdit || target == lastNameEdit
|| target == cityEdit || target == phoneNumberEdit) {
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast (event);
if (keyEvent->key() == Qt::Key_Space) {
focusNextChild();
return true ;
}
}
}
return QDialog::eventFilter(target, event);
}
|
- 上面代码实现空格键切换widget, 注意如果实现了需要的功能返回true, 这样就不会事件传递到目标对象.
- QWidget::focusNextChild() --- 使下一个widget具有焦点
- 五个处理与过滤事件的层次
- 我们可以重新实现特定的事件处理函数
- 我们可以重新实现QObject::event()
- 用于在到达特定的事件处理函数之前处理该事件, 例如Tab键. 另外在重实现该函数的过程中需要调用基类的event()用于处理其他事件
- 我们可以在单个对象上安装event filter
- 我们可以在QApplication 对象上安装一个event filter, 所有对象的所有事件都会发送给eventFilter()函数. 常用语调试,
- 我们可以实现QApplication的派生类, 以及重新实现notify().
- Qt调用QApplication::notify()发送事件, 重新实现该函数是获得所有事件的唯一方法, 在任何event filter有机会处理事件之前.
- 许多类型的事件, 包括鼠标和按键事件, 都会进行传递. 当目标对象没有处理该事件, 则其父widget会进行处理该事件. 直到顶层对象.
7.3 Staying Responsive during Intensive Processing
- 调用QApplication::exec()之后, 开始时间循环, 首先是显示和绘制widget, 而后循环运行检查是否有新的事件, 而后分发这些事件至对象.
- 使用多线程处理一些耗时的任务, 以避免界面不响应
- QApplication::processEvents()告诉Qt处理任何待处理的事件, 而后返回控制给调用者.
- 事实上 QApplication::exec只是在while循环内部调用processEvents().
- 在耗时的处理函数中使用qApp->processEvents(); 或者: qApp->processEvents(QEventLoop::ExcludeUserInputEvents); 以避免其他重要的操作如关闭程序. 用于忽略鼠标和按钮事件.
- QProgressDialog是一个进程条, 用于告知当前处理的程度
- QProgressDialog 类: setLabelText, setRange, setModel, setValue, wasCanceled
- 用QApplication::hasPendingEvents和计时器来判断当前是否处于空闲时期
Chapter 8 2D Graphics
- Qt 4.2 "图形视图"结构的核心部分: QGraphicsView, QGraphicsScene, QGraphicsitem 类
8.1 Painting with QPainter
- 在图形设备上绘制, 仅仅需要将设备的指针传递给QPainter构造函数的参数, 如: QPainter painter(this);
- QPainter的三个重要属性: pen, brush, font
- 重要draw函数: drawPoint, drawLine, drawPolyLine, drawPoints, drawLines, drawPolygon, drawRect, drawRoundRect, drawEllipse, drawArc, drawChord, drawPie, drawText, drawPixmap, drawPath
- pen 用于绘制线条和图形轮廓, 由颜色, 宽度, 线条形状, 关联方式组成.
- Cap 和 joint styles: FlatCap, SquareCap, RoundCap, MiterJoint, BevelJoint, RoundJoint
- Line Style: SolidLine, DashLine, DotLint, DashDotLine, DashDotDotLine, NoPen
- brush 表示用于填充几何形状的模式, 由颜色和风格组成. 也可以是一个纹理
- style: SolidPattern, Dense1Pattern, Dense2Pattern, Dense3Pattern, Dense4Pattern, Dense5Pattern, Dense6Pattern, Dense7Pattern, HorPattern, VerPattern, CrossPattern, BDiagPattern, FDiagPattern, DiagCrossPattern, NoBrush
- font 用于绘制文本, 含有许多属性, 其中包含 family和 点大小
- 可以用setPen, setBrush, setFont 修改这些内容
- painter.setRenderHint(QPainter::Antialiasing, true); // 可以实现反锯齿
- QPainterPath 可以指定用于连接基本图形的元素容器: 如直线, 椭圆形, 多边形, 弧, 贝塞尔曲线 和其他绘制路径.
- 一个路径可以表示一条轮廓, 以及该轮廓所标示的面积, 该面积可以用笔刷填充.
- gradient fill是一个可选的单色填充方式, Gradient依赖于颜色插值以得到两个颜色之间的平滑转换. 常用于生成3D效果.
- Qt 提供三个gradient类型: 线性, 圆锥体的(conical), 径向的(radial)
- 线性: 有两个控制点用于定义, 通过一系列线上的"颜色点"来连接两个点. 如:
?
1
2
3
4
|
QLinearGradient gradient(50, 100, 300, 350);
gradient.setColorAt(0.0, Qt::white);
gradient.setColorAt(0.2, Qt::green);
gradient.setColorAt(1.0, Qt::black);
|
- Radial: 通过中心点(Xc,Yc), 半径r, 焦点(Xf, Yf)来定义,
- Conical: 通过中心点(Xc, Yc)和角度a来定义.
- 其他的属性:
- background brush, brush origin, clip region(可以绘制的区域),
- viewport, window, world transform --- 可以确定逻辑QPainter坐标映射到物理绘制设备坐标. 缺省情况两者一致
- composition mode --- 定义新绘制的像素如何与原有像素相互影响. 默认是alpha 混合.
- 我们可以通过 save()保存当前painter的状态, 通过restore()还原.
8.2 Coordinate System Transformations
- 缺省的坐标系统, 左上角(0, 0), X轴正向方向向右, Y轴正向方向向下, 每个像素的大小为 1x1
- 一个像素的中心是0.5, 只有当禁用反锯齿的时候+0.5.
- 如果开启了反锯齿, 在点(100, 100)绘制黑色. 则四个点(99.5, 99.5), (99.5, 100.5), (100.5, 99.5), (100.5, 100.5)为灰色.
- 如果不想使用这个效果, 则移动QPainter (+0.5, +0.5), 或者指定半个像素的坐标
- 窗口(逻辑) --- 视图(物理)
- 窗口-视图机制 可以让绘制代码独立于绘制设备的分辨率和大小
?
1 |
painter.setWindow(-50, -50, 100, 100); // 从(-50, -50)到(50, 50), 中心点为(0, 0) |
- 如果我们想要在45度角绘制文本
?
1
2
3
4
|
QTransform transform;
transform.rotate(+45.0);
painter.setWorldTransform(transform);
painter.drawText(pos, tr( "Sales" ));
|
- 指定转换的简单方法是使用QPainter的translate(), scale(), rotate()和shear()方法
- 如果经常使用同一个转换, 可以保存一个QTransform, 在需要的时候使用
- QTimer调用setSingleShot(true), 表示在时间结束之后只发送一次time out信息. 否则缺省计时器会重复启发直至他们停止或者销毁.
- qBound() 函数, 确保第二个参数的值在第一个和第三个参数之间.
- 本节的例子使用QConicalGradient(QRadialGradient, QLinearGradient)实现了非常漂亮的效果, 这几个可以当作笔刷使用
8.3 High-Quality Rendering with QImage
- 有时我们需要权衡速度和精确度之间的关系. 当精确度比效率更重要时, 我们将绘制到QImage, 而后将结果拷贝至屏幕. 这将使用Qt的内置绘制引擎.
- 唯一的限制是QImage的创建参数为QImage::Format_RGB32 或 QImage::Format_ARGB32_Premultiplied
- "premultiplied ARPG32" 格式表示红,绿,蓝频道带有多个alpha频道. 代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
|
void MyWidget::paintEvent(QPaintEvent *event)
{
QImage image(size(), QImage::Format_ARGB32_Premultiplied);
QPainter imagePainter(&image);
imagePainter.initFrom( this );
imagePainter.setRenderHint(QPainter::Antialiasing, true );
imagePainter.eraseRect(rect());
draw(&imagePainter);
imagePainter.end();
QPainter widgetPainter( this );
widgetPainter.drawImage(0, 0, image);
}
|
- 上面代码先用QPainter绘制QImage, 而后绘制到屏幕
- 一个非常好的功能就是Qt图形引擎支持合成模式, 源和目标像素可以混合. 在所有的绘制操作都可以实现这点, 如笔, 笔刷, 渐进和图像绘制
- 缺省的合成模式为: QImage::CompositionMode_SourceOver, 表示源像素会覆盖在目标像素之上, 根据alpha值设置透明度.
- 可通过 QPainter::setCompositionMode() 来设置合成模式. 如:
?
1
2
3
4
|
QImage resultImage = checkerPatternImage;
QPainter painter(&resultImage);
painter.setCompositionMode(QPainter::CompositionMode_Xor);
painter.drawImage(0, 0, butterflyImage);
|
- XOR 源和目标. 注意XOR模式对Alpha也是有效的.
8.4 Item-Based Rendering with Graphics View
- QPainter对于绘制自定义widget和比较少的条目时是比较理想的方法
- 绘制大量的条目和内容时的解决方案
- 图形视图结构包含场景, 由QGraphicsScene类表示, 场景中的项目, 由QGraphicsItem的子类表示.
- 通过在视图中显示场景. 由QGraphicsView类表示视图. 相同的场景可以在多个视图中显示. 如显示一个巨大场景的不同部分, 不同的转换.
- 预定义好的QGraphicsItem派生类, 如QGraphicsLineItem, QGraphicsPixmapItem, QGraphicsSimpleTextItem, QGraphicsTextItem
- QGraphicsScene控制图形元素的结合, 有三个layer, 背景层, 元素层, 前景层. 背景和前景层通常由QBrushes指定
- 我们可以基于pixmap创建一个纹理QBrush作为背景. 前景则可以设置半透明等
- 视图则管理场景. 视图可用内置的2D绘制引擎, 也可以用Opengl. 使用setViewport()来调用opengl
- 视图可以用打印一个场景或者场景的一部分.
- 这个结构使用三个不同的坐标系统 - 视图坐标, 场景坐标和项目(item)坐标 -- 带有一个坐标系统向另一个坐标系统映射的功能.
- 视图坐标系统位于QGraphicsView的视图内部. 场景坐标系统是逻辑坐标系统, 用于防治场景上的顶层项目(item).
- 项目坐标系统用于指定每个项目, 且其中心为(0, 0)本地坐标.
- 事实上我们只关心系统坐标放置顶层项目, 项目坐标放置子项目.
- 为了介绍图形视图, 本例使用了两个例子, 第一个例子为简单的图表编辑器,
- 第二个例子为注解映射程序显示如何处理大量的图形对象, 如何有效的渲染以及缩放
- QGraphicsItem不是QObject的派生类, 如果想要使用signal和slot, 可以实现多个继承, 其中一个基类为QObject. 其可以调用函数 setLine 绘制直线.
- Q_DECLARE_TR_FUNCTIONS()宏用于添加一个tr()函数至该类. 即便它不是QOjbect的派生类, 这样可以让我们简单的使用tr(), 而不是QObject::tr() 或者 QApplication::translate()
- 当我们实现了QGraphicsItem的派生类时, 如果想要手工绘制内容, 则需要重新实现boundingRect()和paint()
- 如果我们不重新实现 shape(), 基类则会回去调用boundingRect, 所以我们重新实现shape()用于返回更精确的形状, 该形状可以考虑到节点的圆角角
- graphic view结构使用围绕矩形(bounding rectangle)确定一个项目绘制的区域. 这能够快速的显示任意大的场景, 虽然在每个时候只能显示该场景的一部分
- 形状(shape)则确定某点是否在一个项目之内, 或者两个项目是否相互碰撞.
- 本例图表应用程序, 我们将提供属性对话框用于编辑节点的位置, 颜色和文本. 通过双击节点则可修改文本.
- 下面的代码删除该节点的所有Link, 而无论该Link是否被销毁了, 如果使用qDeleteAll() 则会产生一些副作用.
?
1
2
|
foreach (Link *link, myLinks)
delete link;
|
- 当一个项目的围绕矩形会发生变化之时(由于新的文本也许会大于或小于当前文本),
- 我们必须在之前立即调用 prepareGeometryChange(), 这样便于影响项目的围绕矩形.
- 修改颜色的时候不需要调用 prepareGeometryChange(), 因为这不会影响到项目的围绕矩形大小
- 求一个节点的矩形框
?
1
2
3
4
5
6
|
const int Padding = 8;
QFontMetricsF metrics = qApp->font();
QRectF rect = metrics.boundingRect(myText);
rect.adjust(-Padding, -Padding, +Padding, +Padding);
rect.translate(-rect.center());
return rect;
|
- QFontMetrics计算的围绕矩形左上角坐标总为(0, 0)
- 可使用QPainterPath精确的描述圆角矩形, 可以使得当鼠标位于角落且不在圆角矩形之内时, 不能够选择该矩形
- QStyleOptionGraphicsItem是一个不经常使用的类, 提供了几个公有成员变量,
- 如当前layout方向, 字体metrics, palette, 矩形, 状态, 转换矩阵, 以及细节层次Lod,
- 重新实现 QGraphicsItem::itemChange, 当项目变化时作出一些反应
- 创建一个QGraphicsScene, 而后创建一个QGraphicsView来显示它.
- 选择的项目可以通过按Ctrl键多选. 设置模式QGraphicsView::RubberBandDrag, 表示可以通过鼠标划拉一个矩形多选项目: view->setDragMode(QGraphicsView::RubberBandDrag);
- QGraphicsScene 可以发射信号 selectionChanged, 调用addItem 增加项目, clearSelection取消选择, selectedItems返回选中项目的列表
- QGraphicsItem可调用setPos设置位置, setSelected表示选中与否
- QGraphicsView 可调用removeAction 移除菜单, 调用addAction添加菜单
- QMutableListIterator 用于遍历一个列表; qDeleteAll 用于删除一列表所有元素
- QColorDialog::getColor() 调用颜色对话框
- QApplication::clipboard()->setText(str); --- 使用剪贴板
- QApplication::clipboard()->text(); --- 得到剪贴板文本
- QString::split --- 分割字符串为QStringList
- QStringList 用mid 得到部分的列表, 用join合起来成一个字符串
- 本节第二个例子
- QGraphicsScene: setBackgroundBrush设置背景笔刷
- Lod可以表示为缩放因子, QStyleOptionGraphicsItem::levelOfDetail 表示为其缩放因子
- 使用 ItemIgnoresTransformations 标志可以忽略缩放, 不会跟随View的缩放而更改该Item的大小
- QGraphicsView派生一个类, 实现特定的特色.
- 调用setDragMode, 可设置拖曳模式, 如 setDragMode(ScrollHandDrag);
- 实现wheelEvent函数, 可实现鼠标滚轮事件. 而后调用QGraphicsView::scale函数实现缩放
- 我们的graphic view有许多的功能, 如可以拖曳, 图形项目有tooltip和自定义光标.
- 可通过给项目设置QGraphicsItemAnimations和QTimeLine 来实现动画.
8.5 Printing
- 对于Qt来说, 打印和QWidget, QPixmap, QImage绘制一样, 由以下步骤组成
- 创建一个QPrinter用于绘制设备
- 弹出QPrintDialog, 让用户选择一个打印机并设置一些属性.
- 创建一个QPainter, 让其对QPrinter进行操作
- 使用QPainter绘制一个页面
- 调用QPrinter::newPage() 绘制下一个页面
- 重复第四步和第五步直至所有页面打印完毕
- 在Windows和Mac OS中, QPrinter使用系统的打印驱动. 在Unix中, 它生成PostScript并将其发送给ip或ipr(或者使用QPrinter::setPrintProgram()发送给程序集)
- QPrinter也可以通过调用setOutputFromat(QPrinter::PdfFormat)生成PDF文件
- 通过QPrintDialog的对象调用exec()来执行打印对话框.
- 可将QPrinter对象作为参数传送给QPainter, 而后QPainter绘制图像实现打印一个图像. drawImage
- 如果要打印一个graphics view scenes也很简单, 将QPrinter作为第一个参数传送给QGraphicsScene::render()或者QGraphicsView::render().
- 如果只想绘制场景的一部分, 则将目标矩形(打印页面的位置)和源矩形作为参数传送给render()的可选参数.
- 两个处理打印多页面的方法
- 我们可以将我们的数据转换成HTML, 而后使用QTextDocument渲染它. 使用Qt的富文本引擎
- 手动执行绘制和页面中断
- 富文本方式:
?
1
2
3
|
QTextDocument textDocument;
textDocument.setHtml(html);
textDocument.print(&printer);
|
- 本节还演示了如何给一个QStringList进行分页打印. 写了一个函数, 根据高度进行分页
?
1
2
|
void PrintWindow::paginate(QPainter *painter, QList *pages,
const QStringList &entries)
|
- 在例子中函数 int PrintWindow::entryHeight(QPainter *painter, const QString &entry) 计算每个条目的高度 其使用 QPainter::boundingRect() 计算垂直高度.
- 通过QPrintDialog, 用户可以设定拷贝次数, 打印范围, 请求页面顺序(顺序还是反序)
- 可通过调用QPrintDialog::setEnabledOptions() 来确定哪些选项不能由用户设定
|