The Application example shows how to implement a standard GUI application with menus, toolbars, and a status bar. The example itself is a simple text editor program built around QPlainTextEdit.

Nearly all of the code for the Application example is in the MainWindow class, which inherits QMainWindow. QMainWindow provides the framework for windows that have menus, toolbars, dock windows, and a status bar. The application provides File, Edit, and Help entries in the menu bar, with the following popup menus:

The status bar at the bottom of the main window shows a description of the menu item or toolbar button under the cursor.

To keep the example simple, recently opened files aren't shown in the File menu, even though this feature is desired in 90% of applications. Furthermore, this example can only load one file at a time. The SDI and MDI examples show how to lift these restrictions and how to implement recently opened files handling.

值得学习的地方

界面触发的槽函数都定义在private slots区域中。

#include <QtWidgets>

We start by including <QtWidgets>, a header file that contains the definition of all classes in the Qt Core, Qt GUI and Qt Widgets modules. This saves us from the trouble of having to include every class individually.

只在头文件内include必要的头文件

You might wonder why we don't include <QtWidgets> in mainwindow.h and be done with it. The reason is that including such a large header from another header file can rapidly degrade performances. Here, it wouldn't do any harm, but it's still generally a good idea to include only the header files that are strictly necessary from another header file.

一个典型MainWindow的构造函数例子

  1. MainWindow::MainWindow()
  2. : textEdit(new QPlainTextEdit)
  3. {
  4. setCentralWidget(textEdit);
  5.  
  6. createActions();
  7. createStatusBar();
  8.  
  9. readSettings();
  10.  
  11. connect(textEdit->document(), &QTextDocument::contentsChanged,
  12. this, &MainWindow::documentWasModified);
  13.  
  14. #ifndef QT_NO_SESSIONMANAGER
  15. QGuiApplication::setFallbackSessionManagementEnabled(false);
  16. connect(qApp, &QGuiApplication::commitDataRequest,
  17. this, &MainWindow::commitData);
  18. #endif
  19.  
  20. setCurrentFile(QString());
  21. setUnifiedTitleAndToolBarOnMac(true);
  22. }

In the constructor, we start by creating a QPlainTextEdit widget as a child of the main window (the this object). Then we call QMainWindow::setCentralWidget() to tell that this is going to be the widget that occupies the central area of the main window, between the toolbars and the status bar.

Then we call createActions() and createStatusBar(), two private functions that set up the user interface. After that, we call readSettings() to restore the user's preferences.

We establish a signal-slot connection between the QPlainTextEdit's document object and our documentWasModified() slot. Whenever the user modifies the text in the QPlainTextEdit, we want to update the title bar to show that the file was modified.

At the end, we set the window title using the private setCurrentFile() function. We'll come back to this later.

关闭前询问是否保存

  1. void MainWindow::closeEvent(QCloseEvent *event)
  2. {
  3. if (maybeSave()) {
  4. writeSettings();
  5. event->accept();
  6. } else {
  7. event->ignore();
  8. }
  9. }

When the user attempts to close the window, we call the private function maybeSave() to give the user the possibility to save pending changes. The function returns true if the user wants the application to close; otherwise, it returns false. In the first case, we save the user's preferences to disk and accept the close event; in the second case, we ignore the close event, meaning that the application will stay up and running as if nothing happened.

maybeSave()

  1. bool MainWindow::maybeSave()
  2. {
  3. if (!textEdit->document()->isModified())
  4. return true;
  5. const QMessageBox::StandardButton ret
  6. = QMessageBox::warning(this, tr("Application"),
  7. tr("The document has been modified.\n"
  8. "Do you want to save your changes?"),
  9. QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
  10. switch (ret) {
  11. case QMessageBox::Save:
  12. return save();
  13. case QMessageBox::Cancel:
  14. return false;
  15. default:
  16. break;
  17. }
  18. return true;
  19. }

The maybeSave() function is called to save pending changes. If there are pending changes, it pops up a QMessageBox giving the user to save the document. The options are QMessageBox::Yes, QMessageBox::No, and QMessageBox::Cancel. The Yes button is made the default button (the button that is invoked when the user presses Return) using the QMessageBox::Default flag; the Cancel button is made the escape button (the button that is invoked when the user presses Esc) using the QMessageBox::Escape flag. The maybeSave() function returns true in all cases, except when the user clicks Cancel or saving the file fails. The caller must check the return value and stop whatever it was doing if the return value is false.

  1. void MainWindow::newFile()
  2. {
  3. if (maybeSave()) {
  4. textEdit->clear();
  5. setCurrentFile(QString());
  6. }
  7. }
  1. void MainWindow::open()
  2. {
  3. if (maybeSave()) {
  4. QString fileName = QFileDialog::getOpenFileName(this);
  5. if (!fileName.isEmpty())
  6. loadFile(fileName);
  7. }
  8. }

创建/打开新文件之前,都要先用maybeSave来检查

save()和saveAs()

  1. bool MainWindow::save()
  2. {
  3. if (curFile.isEmpty()) {
  4. return saveAs();
  5. } else {
  6. return saveFile(curFile);
  7. }
  8. }
  1. bool MainWindow::saveAs()
  2. {
  3. QFileDialog dialog(this);
  4. dialog.setWindowModality(Qt::WindowModal);
  5. dialog.setAcceptMode(QFileDialog::AcceptSave);
  6. if (dialog.exec() != QDialog::Accepted)
  7. return false;
  8. return saveFile(dialog.selectedFiles().first());
  9. }

About对话框

  1. void MainWindow::about()
  2. {
  3. QMessageBox::about(this, tr("About Application"),
  4. tr("The <b>Application</b> example demonstrates how to "
  5. "write modern GUI applications using Qt, with a menu bar, "
  6. "toolbars, and a status bar."));
  7. }

The application's About box is done using one statement, using the QMessageBox::about() static function and relying on its support for an HTML subset. The tr() call around the literal string marks the string for translation. It is a good habit to call tr() on all user-visible strings, in case you later decide to translate your application to other languages. The Internationalization with Qt overview covers tr() in more detail.

让窗口表现是否被内容是否被修改的状态(win下是窗口标题栏最后一个字符后面多了一个*)

  1. void MainWindow::documentWasModified()
  2. {
  3. setWindowModified(textEdit->document()->isModified());
  4. }

The documentWasModified() slot is invoked each time the text in the QPlainTextEdit changes because of user edits. We call QWidget::setWindowModified() to make the title bar show that the file was modified. How this is done varies on each platform.

菜单栏/工具栏/QAction

  1. void MainWindow::createActions()
  2. {
  3.  
  4. QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
  5. QToolBar *fileToolBar = addToolBar(tr("File"));
  6. const QIcon newIcon = QIcon::fromTheme("document-new", QIcon(":/images/new.png"));
  7. QAction *newAct = new QAction(newIcon, tr("&New"), this);
  8. newAct->setShortcuts(QKeySequence::New);
  9. newAct->setStatusTip(tr("Create a new file"));
  10. connect(newAct, &QAction::triggered, this, &MainWindow::newFile);
  11. fileMenu->addAction(newAct);
  12. fileToolBar->addAction(newAct);
  13.  
  14. const QIcon openIcon = QIcon::fromTheme("document-open", QIcon(":/images/open.png"));
  15. QAction *openAct = new QAction(openIcon, tr("&Open..."), this);
  16. openAct->setShortcuts(QKeySequence::Open);
  17. openAct->setStatusTip(tr("Open an existing file"));
  18. connect(openAct, &QAction::triggered, this, &MainWindow::open);
  19. fileMenu->addAction(openAct);
  20. fileToolBar->addAction(openAct);
  21. ...
  22. QAction *aboutQtAct = helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
  23. aboutQtAct->setStatusTip(tr("Show the Qt library's About box"));

The createActions() private function, which is called from the MainWindow constructor, creates QActions and populates the menus and two toolbars. The code is very repetitive, so we show only the actions corresponding to File|New, File|Open, and Help|About Qt.

A QAction is an object that represents one user action, such as saving a file or invoking a dialog. An action can be put in a QMenu or a QToolBar, or both, or in any other widget that reimplements QWidget::actionEvent().

An action has a text that is shown in the menu, an icon, a shortcut key, a tooltip, a status tip (shown in the status bar), a "What's This?" text, and more. It emits a triggered() signal whenever the user invokes the action (e.g., by clicking the associated menu item or toolbar button).

Instances of QAction can be created by passing a parent QObject or by using one of the convenience functions of QMenu, QMenuBar or QToolBar. We create the actions that are in a menu as well as in a toolbar parented on the window to prevent ownership issues. For actions that are only in the menu, we use the convenience function QMenu::addAction(), which allows us to pass text, icon and the target object and its slot member function.

Creating toolbars is very similar to creating menus. The same actions that we put in the menus can be reused in the toolbars. After creating the action, we add it to the toolbar using QToolBar::addAction().

The code above contains one more idiom that must be explained. For some of the actions, we specify an icon as a QIcon to the QAction constructor. We use QIcon::fromTheme() to obtain the correct standard icon from the underlying window system. If that fails due to the platform not supporting it, we pass a file name as fallback. Here, the file name starts with :. Such file names aren't ordinary file names, but rather path in the executable's stored resources. We'll come back to this when we review the application.qrc file that's part of the project.

用户偏好设置的载入和保存

  1. void MainWindow::readSettings()
  2. {
  3. QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
  4. const QByteArray geometry = settings.value("geometry", QByteArray()).toByteArray();
  5. if (geometry.isEmpty()) {
  6. const QRect availableGeometry = QApplication::desktop()->availableGeometry(this);
  7. resize(availableGeometry.width() / , availableGeometry.height() / );
  8. move((availableGeometry.width() - width()) / ,
  9. (availableGeometry.height() - height()) / );
  10. } else {
  11. restoreGeometry(geometry);
  12. }
  13. }

The readSettings() function is called from the constructor to load the user's preferences and other application settings. The QSettings class provides a high-level interface for storing settings permanently on disk. On Windows, it uses the (in)famous Windows registry; on macOS, it uses the native XML-based CFPreferences API; on Unix/X11, it uses text files.

The QSettings constructor takes arguments that identify your company and the name of the product. This ensures that the settings for different applications are kept separately.

We use QSettings::value() to extract the value of the geometry setting. The second argument to QSettings::value() is optional and specifies a default value for the setting if there exists none. This value is used the first time the application is run.

We use QWidget::saveGeometry() and Widget::restoreGeometry() to save the position. They use an opaque QByteArray to store screen number, geometry and window state.

  1. void MainWindow::writeSettings()
  2. {
  3. QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
  4. settings.setValue("geometry", saveGeometry());
  5. }

The writeSettings() function is called from closeEvent(). Writing settings is similar to reading them, except simpler. The arguments to the QSettings constructor must be the same as in readSettings().

载入文件和保存文件(QFile/QTextStream

  1. void MainWindow::loadFile(const QString &fileName)
  2. {
  3. QFile file(fileName);
  4. if (!file.open(QFile::ReadOnly | QFile::Text)) {
  5. QMessageBox::warning(this, tr("Application"),
  6. tr("Cannot read file %1:\n%2.")
  7. .arg(QDir::toNativeSeparators(fileName), file.errorString()));
  8. return;
  9. }
  10.  
  11. QTextStream in(&file);
  12. #ifndef QT_NO_CURSOR
  13. QApplication::setOverrideCursor(Qt::WaitCursor);
  14. #endif
  15. textEdit->setPlainText(in.readAll());
  16. #ifndef QT_NO_CURSOR
  17. QApplication::restoreOverrideCursor();
  18. #endif
  19.  
  20. setCurrentFile(fileName);
  21. statusBar()->showMessage(tr("File loaded"), );
  22. }

In loadFile(), we use QFile and QTextStream to read in the data. The QFile object provides access to the bytes stored in a file.

We start by opening the file in read-only mode. The QFile::Text flag indicates that the file is a text file, not a binary file. On Unix and macOS, this makes no difference, but on Windows, it ensures that the "\r\n" end-of-line sequence is converted to "\n" when reading.

If we successfully opened the file, we use a QTextStream object to read in the data. QTextStream automatically converts the 8-bit data into a Unicode QString and supports various encodings. If no encoding is specified, QTextStream assumes the file is written using the system's default 8-bit encoding (for example, Latin-1; see QTextCodec::codecForLocale() for details).

Since the call to QTextStream::readAll() might take some time, we set the cursor to be Qt::WaitCursor for the entire application while it goes on.

At the end, we call the private setCurrentFile() function, which we'll cover in a moment, and we display the string "File loaded" in the status bar for 2 seconds (2000 milliseconds).

  1. bool MainWindow::saveFile(const QString &fileName)
  2. {
  3. QFile file(fileName);
  4. if (!file.open(QFile::WriteOnly | QFile::Text)) {
  5. QMessageBox::warning(this, tr("Application"),
  6. tr("Cannot write file %1:\n%2.")
  7. .arg(QDir::toNativeSeparators(fileName),
  8. file.errorString()));
  9. return false;
  10. }
  11.  
  12. QTextStream out(&file);
  13. #ifndef QT_NO_CURSOR
  14. QApplication::setOverrideCursor(Qt::WaitCursor);
  15. #endif
  16. out << textEdit->toPlainText();
  17. #ifndef QT_NO_CURSOR
  18. QApplication::restoreOverrideCursor();
  19. #endif
  20.  
  21. setCurrentFile(fileName);
  22. statusBar()->showMessage(tr("File saved"), );
  23. return true;
  24. }

Saving a file is very similar to loading one. Here, the QFile::Text flag ensures that on Windows, "\n" is converted into "\r\n" to conform to the Windows convension.

  1. void MainWindow::setCurrentFile(const QString &fileName)
  2. {
  3. curFile = fileName;
  4. textEdit->document()->setModified(false);
  5. setWindowModified(false);
  6.  
  7. QString shownName = curFile;
  8. if (curFile.isEmpty())
  9. shownName = "untitled.txt";
  10. setWindowFilePath(shownName);
  11. }

The setCurrentFile() function is called to reset the state of a few variables when a file is loaded or saved, or when the user starts editing a new file (in which case fileName is empty). We update the curFile variable, clear the QTextDocument::modified flag and the associated QWidget:windowModified flag, and update the window title to contain the new file name (or untitled.txt).

关于windowFilePath

This property holds the file path associated with a widget

This property only makes sense for windows. It associates a file path with a window. If you set the file path, but have not set the window title, Qt sets the window title to the file name of the specified path, obtained using QFileInfo::fileName().

The main() Function中的QCommandLineParser

  1. #include <QApplication>
  2. #include <QCommandLineParser>
  3. #include <QCommandLineOption>
  4.  
  5. #include "mainwindow.h"
  6.  
  7. int main(int argc, char *argv[])
  8. {
  9. Q_INIT_RESOURCE(application);
  10.  
  11. QApplication app(argc, argv);
  12. QCoreApplication::setOrganizationName("QtProject");
  13. QCoreApplication::setApplicationName("Application Example");
  14. QCoreApplication::setApplicationVersion(QT_VERSION_STR);
  15. QCommandLineParser parser;
  16. parser.setApplicationDescription(QCoreApplication::applicationName());
  17. parser.addHelpOption();
  18. parser.addVersionOption();
  19. parser.addPositionalArgument("file", "The file to open.");
  20. parser.process(app);
  21.  
  22. MainWindow mainWin;
  23. if (!parser.positionalArguments().isEmpty())
  24. mainWin.loadFile(parser.positionalArguments().first());
  25. mainWin.show();
  26. return app.exec();
  27. }

The main() function for this application is typical of applications that contain one main window:

The main function uses QCommandLineParser to check whether some file argument was passed to the application and loads it via MainWindow::loadFile().

资源文件

As you will probably recall, for some of the actions, we specified icons with file names starting with : and mentioned that such file names aren't ordinary file names, but path in the executable's stored resources. These resources are compiled

The resources associated with an application are specified in a .qrc file, an XML-based file format that lists files on the disk. Here's the application.qrc file that's used by the Application example:

  1. <!DOCTYPE RCC><RCC version="1.0">
  2. <qresource>
  3. <file>images/copy.png</file>
  4. <file>images/cut.png</file>
  5. <file>images/new.png</file>
  6. <file>images/open.png</file>
  7. <file>images/paste.png</file>
  8. <file>images/save.png</file>
  9. </qresource>
  10. </RCC>

The .png files listed in the application.qrc file are files that are part of the Application example's source tree. Paths are relative to the directory where the application.qrc file is located (the mainwindows/application directory).

The resource file must be mentioned in the application.pro file so that qmake knows about it:

  1. RESOURCES = application.qrc

qmake will produce make rules to generate a file called qrc_application.cpp that is linked into the application. This file contains all the data for the images and other resources as static C++ arrays of compressed binary data. See The Qt Resource System for more information about resources.

【Qt官方例程学习笔记】Application Example(构成界面/QAction/退出时询问保存/用户偏好载入和保存/文本文件的载入和保存/QCommandLineParser解析运行参数)的更多相关文章

  1. 【Qt官方例程学习笔记】Raster Window Example(画笔的平移/旋转/缩放应用)

    这个例子显示了如何使用QPainter渲染一个简单的QWindow. 值得学习的内容 <QtGui>头文件 #include <QtGui>就可以使用Qt GUI模块中的所有类 ...

  2. 【Qt官方例程学习笔记】Analog Clock Window Example (画笔的平移/旋转/缩放应用)

    这个例子演示了如何使用QPainter的转换和缩放特性来简化绘图. 值得学习的: 定时器事件ID检查: 在定时器事件中检查定时器id是比较好的实践. QPainter抗锯齿: We call QPai ...

  3. 【Qt官方例程学习笔记】Address Book Example(代理模型)

    地址簿示例展示了如何使用代理模型在单个模型的数据上显示不同的视图. 本例提供了一个地址簿,允许按字母顺序将联系人分组为9组:ABC.DEF.GHI.…,VW,…XYZ.这是通过在同一个模型上使用多个视 ...

  4. 【Qt官方例程学习笔记】Getting Started Programming with Qt Widgets

    创建一个QApplication对象,用于管理应用程序资源,它对于任何使用了Qt Widgets的程序都必要的.对于没有使用Qt Widgets 的GUI应用,可以使用QGuiApplication代 ...

  5. (转)Qt Model/View 学习笔记 (七)——Delegate类

    Qt Model/View 学习笔记 (七) Delegate  类 概念 与MVC模式不同,model/view结构没有用于与用户交互的完全独立的组件.一般来讲, view负责把数据展示 给用户,也 ...

  6. (转)Qt Model/View 学习笔记 (五)——View 类

    Qt Model/View 学习笔记 (五) View 类 概念 在model/view架构中,view从model中获得数据项然后显示给用户.数据显示的方式不必与model提供的表示方式相同,可以与 ...

  7. Note | PyTorch官方教程学习笔记

    目录 1. 快速入门PYTORCH 1.1. 什么是PyTorch 1.1.1. 基础概念 1.1.2. 与NumPy之间的桥梁 1.2. Autograd: Automatic Differenti ...

  8. Qt官方开发环境生成的exe发布方式--使用windeployqt(windeployqt是单独的程序,放在低版本qt4目录下也可以运行的)

    Qt 官方开发环境使用的动态链接库方式,在发布生成的exe程序时,需要复制一大堆 dll,如果自己去复制dll,很可能丢三落四,导致exe在别的电脑里无法正常运行.因此 Qt 官方开发环境里自带了一个 ...

  9. Android API Guides 学习笔记---Application Fundamentals(一)

    今天开始学习google官网上的API guides ,主要读了Application Fundamentals这一章节,此章节介绍了一个App的基本组成,共包括四大部分内容. 1.      App ...

随机推荐

  1. mysql中的内连接,外连接实例详解

    内连接: 只连接匹配的行左外连接: 包含左边表的全部行(不管右边的表中是否存在与它们匹配的行),以及右边表中全部匹配的行右外连接: 包含右边表的全部行(不管左边的表中是否存在与它们匹配的行),以及左边 ...

  2. java:stack栈: Stack 类表示后进先出(LIFO)的对象堆栈

    //Stack 类表示后进先出(LIFO)的对象堆栈 //它提供了通常的 push 和 pop 操作,以及取栈顶点的 peek 方法.测试堆栈是否为空的 empty 方法.在堆栈中查找项并确定到栈顶距 ...

  3. cluster KMeans need preprocessing scale????

    Out: n_digits: 10, n_samples 1797, n_features 64 ___________________________________________________ ...

  4. phpStudy如何修改端口及WWW目录

    phpStudy如何修改端口及WWW目录 phpStudy如何修改端口 请使用『其他选项菜单』-『phpStudy设置』-『端口常规设置』.

  5. hibernate复习第(一)天

    首先导入jar. 这个版本是3.2.5 开发流程: 1.由Domain object ->mapping ->db (官方推荐) 2.由DB开始,使用工具生成mapping和Domain ...

  6. 【BZOJ 3238】差异 后缀自动机+树形DP

    题意 给定字符串,令$s_i$表示第$i$位开始的后缀,求$\sum_{1\le i < j \le n} len(s_i)+len(s_j)-2\times lcp(s_i,s_j)$ 先考虑 ...

  7. 2017-2018-1 20179215《Linux内核原理与分析》第四周作业

    本次的实验是使用gdb跟踪调试内核从start_kernel到init进程启动,并分析启动的过程. 1.首先是在实验楼虚拟机上进行调试跟踪的过程. cd LinuxKernel qemu -kerne ...

  8. vi查找替换命令详解

    一.查找 查找命令 /pattern<Enter> :向下查找pattern匹配字符串 ?pattern<Enter>:向上查找pattern匹配字符串 使用了查找命令之后,使 ...

  9. JavaScript运行机制与setTimeout

    前段时间,老板交给了我一个任务:通过setTimeout来延后网站某些复杂资源的请求.正好借此机会,将JavaScript运行机制和setTimeout重新认真思考一遍,并将我对它们的理解整理如下. ...

  10. 找工作——JVM内存管理

    1. JVM类加载机制 类从被加载到虚拟机内存开始,到卸载出内存为止,它的整个生命周期包括:加载.连接(验证.准备.解析).初始化.使用和卸载阶段. 加载:根据查找路径找到对应的class文件,然后倒 ...