Qt Dock Widgets 官方示例的翻译

Qt Dock Widgets 官方示例的翻译

Dock Widgets Example 介绍:

Dock Widgets 示例程序描述的有两个技术要点:

  • 1、如何添加Dock 窗体到应用程序中。
  • 2、如何使用Qt的富文本引擎。

在这里插入图片描述

该示例应用程序描述的是一个简单的处理商业邮件的模板程序。在两个Dock 窗体中分别显示了客户的信息 和 常用语。通过单击Dock中的列表,将自定添加选中的信息添加到邮件模板中。当然,撤销按钮可以撤去邮件模板中错误的或者不需要的信息。一旦邮件完成,可以直接答应或者保存为HTML格式。

MainWindow Class 定义:

  class MainWindow : public QMainWindow
  {
      Q_OBJECT

  public:
      MainWindow();

  private slots:
      void newLetter();
      void save();
      void print();
      void undo();
      void about();
      void insertCustomer(const QString &customer);
      void addParagraph(const QString &paragraph);

  private:
      void createActions();
      void createStatusBar();
      void createDockWindows();

      QTextEdit *textEdit;
      QListWidget *customerList;
      QListWidget *paragraphsList;

      QMenu *viewMenu;
  };

接着,我们来一个个来了解每个函数的功能。

MainWindow Class 关联的相关头文件

#include
#if defined(QT_PRINTSUPPORT_LIB)
#include <QtPrintSupport/qtprintsupportglobal.h>
#if QT_CONFIG(printdialog)
#include
#endif
#endif

#include “mainwindow.h”

相关函数功能的介绍:

一开始我们包含了 ,QWidget类是所有用户界面对象的基类。我们同样需要包含mainwindow.h

  MainWindow::MainWindow()
      : textEdit(new QTextEdit)
  {
      setCentralWidget(textEdit);

      createActions();
      createStatusBar();
      createDockWindows();

      setWindowTitle(tr("Dock Widgets"));

      newLetter();
      setUnifiedTitleAndToolBarOnMac(true);
  }

在该构造函数中,我们一开始就创建了QTextEdit Widget。 然后我们调用QMainWindow::setCentralWidget()。 该函数会将 QTextEdit 的所有权传递给 MainWindow ,同时QTextEdit 会占据MainWindow 的中央区域。

然后我们调用 createActions(), createMenus(), createToolBars(), createStatusBar(), and createDockWindows() 来初始化窗体.。
最后,我们调用 setWindowTitle() 来给用用程序设定一个名称, newLetter() 来创建一个邮件模板。

我们 调用 createActions(), createMenus(), createToolBars(), and createStatusBar() 函数,是为了保持和其他的Qt示例程序的风格保持一致。

  void MainWindow::createDockWindows()
  {
      QDockWidget *dock = new QDockWidget(tr("Customers"), this);
      dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
      customerList = new QListWidget(dock);
      customerList->addItems(QStringList()
              << "John Doe, Harmony Enterprises, 12 Lakeside, Ambleton"
              << "Jane Doe, Memorabilia, 23 Watersedge, Beaton"
              << "Tammy Shea, Tiblanka, 38 Sea Views, Carlton"
              << "Tim Sheen, Caraba Gifts, 48 Ocean Way, Deal"
              << "Sol Harvey, Chicos Coffee, 53 New Springs, Eccleston"
              << "Sally Hobart, Tiroli Tea, 67 Long River, Fedula");
      dock->setWidget(customerList);
      addDockWidget(Qt::RightDockWidgetArea, dock);
      viewMenu->addAction(dock->toggleViewAction());

      dock = new QDockWidget(tr("Paragraphs"), this);
      paragraphsList = new QListWidget(dock);
      paragraphsList->addItems(QStringList()
              << "Thank you for your payment which we have received today."
              << "Your order has been dispatched and should be with you "
                 "within 28 days."
              << "We have dispatched those items that were in stock. The "
                 "rest of your order will be dispatched once all the "
                 "remaining items have arrived at our warehouse. No "
                 "additional shipping charges will be made."
              << "You made a small overpayment (less than $5) which we "
                 "will keep on account for you, or return at your request."
              << "You made a small underpayment (less than $1), but we have "
                 "sent your order anyway. We'll add this underpayment to "
                 "your next bill."
              << "Unfortunately you did not send enough money. Please remit "
                 "an additional $. Your order will be dispatched as soon as "
                 "the complete amount has been received."
              << "You made an overpayment (more than $5). Do you wish to "
                 "buy more items, or should we return the excess to you?");
      dock->setWidget(paragraphsList);
      addDockWidget(Qt::RightDockWidgetArea, dock);
      viewMenu->addAction(dock->toggleViewAction());

      connect(customerList, &QListWidget::currentTextChanged,
              this, &MainWindow::insertCustomer);
      connect(paragraphsList, &QListWidget::currentTextChanged,
              this, &MainWindow::addParagraph);
  }

我们创建一个关于客户信息的Dock 窗体,设定了该窗体的标题,我们同时传递“this”指针,这样就指定了MainWindows为父窗体。一般情况下,我们不需要传递,因为当Widgets 陈列出时, 是自动继承父窗体的,但是Dock 窗体是个例外,因为它不是通过layouts 的方式来陈列的。

我们选择限制这个客户信息的Dock 窗体只能显示在左边和右边的Dock区域。关于Dock区域的图示,请看下图:

在这里插入图片描述

用户可以通过鼠标的拓展将Dock 移出,而变成一个自由窗体。通过QDockWidget::setFeatures()来设置Dock窗体是可移动的还是不可移动。

一旦我们创建好了Dock 窗体,创建好了list窗体,并指定list窗体的父窗体为Dock窗体,那么最后我们就可以调用addDockWidget()添加Dock 窗体到MainWindows 窗体中了。

我们依照同样的过程可以创建另一个Dock 窗体,这次我们不限定该窗体的区域位置。

最后,我们通过信号槽事件。关联信号currentTextChanged() ,到两个槽 insertCustomer() ,addParagraph() 。

接着,我们来讨论剩余的其他一些函数的实现。

  void MainWindow::newLetter()
  {
      textEdit->clear();

      QTextCursor cursor(textEdit->textCursor());
      cursor.movePosition(QTextCursor::Start);
      QTextFrame *topFrame = cursor.currentFrame();
      QTextFrameFormat topFrameFormat = topFrame->frameFormat();
      topFrameFormat.setPadding(16);
      topFrame->setFrameFormat(topFrameFormat);

      QTextCharFormat textFormat;
      QTextCharFormat boldFormat;
      boldFormat.setFontWeight(QFont::Bold);
      QTextCharFormat italicFormat;
      italicFormat.setFontItalic(true);

      QTextTableFormat tableFormat;
      tableFormat.setBorder(1);
      tableFormat.setCellPadding(16);
      tableFormat.setAlignment(Qt::AlignRight);
      cursor.insertTable(1, 1, tableFormat);
      cursor.insertText("The Firm", boldFormat);
      cursor.insertBlock();
      cursor.insertText("321 City Street", textFormat);
      cursor.insertBlock();
      cursor.insertText("Industry Park");
      cursor.insertBlock();
      cursor.insertText("Some Country");
      cursor.setPosition(topFrame->lastPosition());
      cursor.insertText(QDate::currentDate().toString("d MMMM yyyy"), textFormat);
      cursor.insertBlock();
      cursor.insertBlock();
      cursor.insertText("Dear ", textFormat);
      cursor.insertText("NAME", italicFormat);
      cursor.insertText(",", textFormat);
      for (int i = 0; i < 3; ++i)
          cursor.insertBlock();
      cursor.insertText(tr("Yours sincerely,"), textFormat);
      for (int i = 0; i < 3; ++i)
          cursor.insertBlock();
      cursor.insertText("The Boss", textFormat);
      cursor.insertBlock();
      cursor.insertText("ADDRESS", italicFormat);
  }

在该函数中,我们清空了QTextEdit。接着我们创建了一个QTextCursor ,我们移动光标到文档的开始处,创建并格式化一个frame。 我们创建一些字符格式和一个表格格式。我们插入一个表格到文档中,并参照上满的格式插入公司名和地址到表格中。接着我们插入包含标记NAME和ADDRESS的邮件“骨架”,我们用“Yours sincerely”,也作为一个标记。

  void MainWindow::insertCustomer(const QString &customer)
  {
      if (customer.isEmpty())
          return;
      QStringList customerList = customer.split(", ");
      QTextDocument *document = textEdit->document();
      QTextCursor cursor = document->find("NAME");
      if (!cursor.isNull()) {
          cursor.beginEditBlock();
          cursor.insertText(customerList.at(0));
          QTextCursor oldcursor = cursor;
          cursor = document->find("ADDRESS");
          if (!cursor.isNull()) {
              for (int i = 1; i < customerList.size(); ++i) {
                  cursor.insertBlock();
                  cursor.insertText(customerList.at(i));
              }
              cursor.endEditBlock();
          }
          else
              oldcursor.endEditBlock();
      }
  }

如果用户单击了客户信息的Dock, 会分割关于客户的信息。然后我们查找标记“NAME”,在该标记中插入客户的命名,同样的,我们查找标记“ADDRESS”,并将此标记替代为客户的地址。
值得注意的是:我们将所有的插入放置在beginEditBlock() 和endEditBlock() 这两个函数之间。

  void MainWindow::addParagraph(const QString &paragraph)
  {
      if (paragraph.isEmpty())
          return;
      QTextDocument *document = textEdit->document();
      QTextCursor cursor = document->find(tr("Yours sincerely,"));
      if (cursor.isNull())
          return;
      cursor.beginEditBlock();
      cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor, 2);
      cursor.insertBlock();
      cursor.insertText(paragraph);
      cursor.insertBlock();
      cursor.endEditBlock();

  }

该函数的功能类似于insertCustomer(). 首先我们寻找标记,这里的标记是“Yours sincerely”,然后在在标记前,插入单击选中的段落文本。同样的这里我们调用beginEditBlock() … endEditBlock()函数对,以便于我们撤销时,把整段添加的都一并撤销。

  void MainWindow::print()
  {
  #if QT_CONFIG(printdialog)
      QTextDocument *document = textEdit->document();
      QPrinter printer;

      QPrintDialog dlg(&printer, this);
      if (dlg.exec() != QDialog::Accepted) {
          return;
      }

      document->print(&printer);
      statusBar()->showMessage(tr("Ready"), 2000);
  #endif
  }

可以看到,Qt 的QTextDocument 类调用打印文档也非常简单。

  void MainWindow::save()
  {
      QMimeDatabase mimeDatabase;
      QString fileName = QFileDialog::getSaveFileName(this,
                          tr("Choose a file name"), ".",
                          mimeDatabase.mimeTypeForName("text/html").filterString());
      if (fileName.isEmpty())
          return;
      QFile file(fileName);
      if (!file.open(QFile::WriteOnly | QFile::Text)) {
          QMessageBox::warning(this, tr("Dock Widgets"),
                               tr("Cannot write file %1:\n%2.")
                               .arg(QDir::toNativeSeparators(fileName), file.errorString()));
          return;
      }

      QTextStream out(&file);
      QGuiApplication::setOverrideCursor(Qt::WaitCursor);
      out << textEdit->toHtml();
      QGuiApplication::restoreOverrideCursor();

      statusBar()->showMessage(tr("Saved '%1'").arg(fileName), 2000);
  }

QTextEdit 提供输出HTML格式的文档。

  void MainWindow::undo()
  {
      QTextDocument *document = textEdit->document();
      document->undo();
  }

如果窗体的焦点放置在QTextEdit,可以通过撤销功能来撤销删除之前添加的文本。

总结:

  1、官方给定的示例对DockWidget 的使用做了简单介绍。
  2、富文本的应用这里也有涉及到。
  3、按照该示例程序的介绍可以很好的训练程序思维。

总之,按部就班的学习示例程序是学好Qt的一项基本策略。

ps:翻译有不当之处,请读者们多多指点,本人纯粹是为了项目和学习的需要,才斗胆拙笔成文。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章