Enter Customer Details是Order Form窗口点击File-New后的结果
当Enter Customer Details的Name或者Address为空时,会弹出Incomplete Form的MessageBox
学习该例子主要是想了解基于QTextCursor的操作方法,文档布局
1
QDialogButtonBox和QMessageBox
A
Enter Customer Details窗口的右下Cancel OkButton就是拿QDialogButtonBox生成的,之所以用这个Qt assitant给出的解释是
Also, a QCheckBox and a QDialogButtonBox are defined; the former to provide the user with the option to receive information on products and offers, and the latter to ensure that buttons used are arranged according to the user's native platform。
除了这点外,用起来也比单独去定义Button方便,体现在这里
buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
                                      | QDialogButtonBox::Cancel);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(verify()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));

Ok Cancel Button可以直接使用,再看下accepted,rejected信号在什么情况下发送
void QDialogButtonBox::accepted ()   [signal]
This signal is emitted when a button inside the button box is clicked, as long as it was defined with the AcceptRole or YesRole.
void QDialogButtonBox::rejected ()   [signal]
This signal is emitted when a button inside the button box is clicked, as long as it was defined with the RejectRole or NoRole.
,而Ok cancel Button的定义:
QDialogButtonBox::Ok 0x00000400 An "OK" button defined with the AcceptRole.
。。。
QDialogButtonBox::Cancel 0x00400000 A "Cancel" button defined with the RejectRole.
此外还有很多Button,如Save Apply等,都定义在
enum QDialogButtonBox::StandardButton
flags QDialogButtonBox::StandardButtons里了。
用起来是不是很方便呢?
不过该类还有很多东西没看懂,如ActionRole等,感觉有空这个类可以单独学习一下。
B
Incomplete Form Widget是一个QMEssgeBOx::warning
之所以两个类归结为一个知识点,是因为QMessageBox与上面的类有一点还是很像的,那就是Button不用定义。。。
QMessageBox的Button都定义在这里了
enum QMessageBox::StandardButton
flags QMessageBox::StandardButtons
跟上面的很像
用起来也非常方便,看下Incomplete Form Widget的相关代码
QMessageBox::StandardButton answer;
     answer = QMessageBox::warning(this, tr("Incomplete Form"),
         tr("The form does not contain all the necessary information.\n"
            "Do you want to discard it?"),
         QMessageBox::Yes | QMessageBox::No);

     if (answer == QMessageBox::Yes)
         reject();

关于warning的定义
Static Public Members
。。。
StandardButton warning ( QWidget * parent, const QString & title, const QString & text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton )
注意是Static的
返回类型即为上面说的枚举类型
2.
QTableView的上边和左边1 2/1 2 3 4的窗口也可以进一步操作
通过
QHeaderView * horizontalHeader () const
拿到对应指针,例如
itemsTable->verticalHeader()->hide();就可以将左边的1 2 3 4隐藏了
3.
QAction * QMenu::addAction ( const QString & text )
This convenience function creates a new action with text. The function adds the newly created action to the menu's list of actions, and returns it.
这个函数非常好用,因为可以自动创建一个action,返回值就是这个action的指针,同时另外几个重载函数有的更加方便
QAction * QMenu::addAction ( const QString & text, const QObject * receiver, const char * member, const QKeySequence & shortcut = 0 )
This is an overloaded function.

This convenience function creates a new action with the text text and an optional shortcut shortcut. The action's triggered() signal is connected to the receiver's member slot. The function adds the newly created action to the menu's list of actions and returns it.
在创建的时候,可以直接指定连接到的槽。
例子里这里分别用到了:
QAction *newAction = fileMenu->addAction(tr("&New..."));
     newAction->setShortcuts(QKeySequence::New);
     printAction = fileMenu->addAction(tr("&Print..."), this, SLOT(printFile()));
     printAction->setShortcuts(QKeySequence::Print);
     printAction->setEnabled(false);
4.
Order Form Widget里主窗口就是QTabWidget
其下显示窗口是一个QTextEdit,通过利用QTextCursor QTextFrame等做的非常好看
因为这个排版之前完全不知道怎么去作,代码涉及了很多的类(都很简单的接口),所以跟着代码做了一遍注释学习了下
     QTextCursor cursor(editor->textCursor());
     cursor.movePosition(QTextCursor::Start);//取得指针并放在最开始的位置
     QTextFrame *topFrame = cursor.currentFrame();//Note that topFrame is the editor's top-level frame and is not shown in the document structure.
     QTextFrameFormat topFrameFormat = topFrame->frameFormat();
     topFrameFormat.setPadding(16);//设置内容的边距,去掉后对比下

     topFrame->setFrameFormat(topFrameFormat);

     QTextCharFormat textFormat;
     QTextCharFormat boldFormat;
     boldFormat.setFontWeight(QFont::Bold);//设置字体,一会会用到

     QTextFrameFormat referenceFrameFormat;
     referenceFrameFormat.setBorder(1);
     referenceFrameFormat.setPadding(8);
     referenceFrameFormat.setPosition(QTextFrameFormat::FloatRight);//该句使得显示位置从右边开始,改成Left后对比下

     referenceFrameFormat.setWidth(QTextLength(QTextLength::PercentageLength, 40));//QTextLength有三种类型,含义如下
 Constant Value Description
 QTextLength::VariableLength 0 The width of the object is variable
 QTextLength::FixedLength 1 The width of the object is fixed
 QTextLength::PercentageLength 2 The width of the object is in percentage of the maximum width
     cursor.insertFrame(referenceFrameFormat);//关于该句的介绍
 QTextFrame * QTextCursor::insertFrame ( const QTextFrameFormat & format )
 Inserts a frame with the given format at the current cursor position(), moves the cursor position() inside the frame, and returns the frame.

 If the cursor holds a selection, the whole selection is moved inside the frame.
 注意给的参数是QTextFramFormat,返回值是QTextFrame,光标会移动到该Frame内


     cursor.insertText("A company", boldFormat);
     cursor.insertBlock();//换行
     cursor.insertText("321 City Street");
     cursor.insertBlock();
     cursor.insertText("Industry Park");
     cursor.insertBlock();
     cursor.insertText("Another country");


     cursor.setPosition(topFrame->lastPosition());

     cursor.insertText(name, textFormat);
     QString line;
     foreach (line, address.split("\n")) {
         cursor.insertBlock();
         cursor.insertText(line);
     }//光标被重新移动回来并且写入name address

     cursor.insertBlock();
     cursor.insertBlock();//twice,一次表示换行,两次才会产生一个空行

     QDate date = QDate::currentDate();
     cursor.insertText(tr("Date: %1").arg(date.toString("d MMMM yyyy")),
                       textFormat);//取日期,insert。。。
     cursor.insertBlock();

     QTextFrameFormat bodyFrameFormat;
     bodyFrameFormat.setWidth(QTextLength(QTextLength::PercentageLength, 100));
     cursor.insertFrame(bodyFrameFormat);
     cursor.insertText(tr("I would like to place an order for the following "
                          "items:"), textFormat);
     cursor.insertBlock();
     cursor.insertBlock();//nserts standard text into the order form.

//接下来是一个
//A QTextTableFormat object, orderTableFormat, is used to hold the type of item and the quantity ordered.
//用来存储orderItems及其数量
     QTextTableFormat orderTableFormat;
     orderTableFormat.setAlignment(Qt::AlignHCenter);
     QTextTable *orderTable = cursor.insertTable(1, 2, orderTableFormat);

     QTextFrameFormat orderFrameFormat = cursor.currentFrame()->frameFormat();
     orderFrameFormat.setBorder(1);
     cursor.currentFrame()->setFrameFormat(orderFrameFormat);
//题外:关于QTextTable的Spacing和Padding在QTextTable类的介绍了有个图

//感觉padding即为外围的边距,spacing即为内部边距
     cursor = orderTable->cellAt(0, 0).firstCursorPosition();
     cursor.insertText(tr("Product"), boldFormat);
     cursor = orderTable->cellAt(0, 1).firstCursorPosition();
     cursor.insertText(tr("Quantity"), boldFormat);
//添加每列的标题P Quantity
//不过这样添加出来的cell是可以在界面上修改的,如何防止这点?
//editor->setReadOnly(true);会使整个页面不可编辑,所以不可用

 for (int i = 0; i < orderItems.count(); ++i) {
         QPair<QString,int> item = orderItems[i];
         int row = orderTable->rows();//当前行数

         orderTable->insertRows(row, 1);//在最后添加额外一行
         cursor = orderTable->cellAt(row, 0).firstCursorPosition();
         cursor.insertText(item.first, textFormat);
         cursor = orderTable->cellAt(row, 1).firstCursorPosition();
         cursor.insertText(QString("%1").arg(item.second), textFormat);
     }
//在这段代码后我加了一段代码测试了下
    cursor.insertText("This is a test");
    cursor.setPosition(cursor.currentFrame()->firstPosition());
    cursor.insertText("another test");
//

//至于cellAt(0,0)处的背景颜色,是这么改变的
 QTextTableCell cell = orderTable->cellAt(0,0);
    QTextCharFormat format = cell.format();
    format.setBackground(Qt::green);
    cell.setFormat(format);
//

//继续程序里的源代码
//The cursor is then moved back to topFrame's lastPosition() and more standard text is inserted.
     cursor.setPosition(topFrame->lastPosition());//返回到topFrame的正确位置开始输入

     cursor.insertBlock();
     cursor.insertText(tr("Please update my records to take account of the "
                          "following privacy information:"));
     cursor.insertBlock();
//Another QTextTable is inserted, to display the customer's preference regarding offers.
     QTextTable *offersTable = cursor.insertTable(2, 2);

     cursor = offersTable->cellAt(0, 1).firstCursorPosition();
     cursor.insertText(tr("I want to receive more information about your "
                          "company's products and special offers."), textFormat);
     cursor = offersTable->cellAt(1, 1).firstCursorPosition();
     cursor.insertText(tr("I do not want to receive any promotional information "
                          "from your company."), textFormat);

     if (sendOffers)
         cursor = offersTable->cellAt(0, 0).firstCursorPosition();
     else
         cursor = offersTable->cellAt(1, 0).firstCursorPosition();

     cursor.insertText("X", boldFormat);

//The cursor is moved to insert "Sincerely" along with the customer's name. More blocks are inserted for spacing purposes. The printAction is enabled to indicate that an order form can now be printed.

     cursor.setPosition(topFrame->lastPosition());
     cursor.insertBlock();
     cursor.insertText(tr("Sincerely,"), textFormat);
     cursor.insertBlock();
     cursor.insertBlock();
     cursor.insertBlock();
     cursor.insertText(name);

     printAction->setEnabled(true);
 }

其实总结下的话,整个界面的形成主要依赖于cursor的移动和insert
移动比如这样
cursor = offersTable->cellAt(0, 1).firstCursorPosition();
或者
cursor.setPosition(topFrame->lastPosition());等等
输入主要依靠QTextCursor的四个函数来完成的
void insertText ( const QString & text, const QTextCharFormat & format )
QTextFrame * insertFrame ( const QTextFrameFormat & format )
void insertBlock ( const QTextBlockFormat & format, const QTextCharFormat & charFormat )
QTextTable * insertTable ( int rows, int columns, const QTextTableFormat & format )
5.
关于这一句分析一下流程
buttonBox 的 accepted()信号会触发DetailsDialog的verify()槽,该槽函数会执行Dialog的accept() or reject()

void QDialog::accept ()   [virtual slot]
Hides the modal dialog and sets the result code to Accepted.
void QDialog::reject ()   [virtual slot]
Hides the modal dialog and sets the result code to Rejected.
void QDialog::done ( int r )   [virtual slot]
Closes the dialog and sets its result code to r. If this dialog is shown with exec(), done() causes the local event loop to finish, and exec() to return r.

//TODO:
最后的print界面用的QPrintDialog来实现,有空可以看下
QTextCursor以及相关还需要进一步学习
openDialog函数里,不执行dialog.exec()
用dialog.show(),窗口没有show出来