A good QMainWindow example

I found useful to understand how to work programmatically with QMainWindow the example "Application" provided by the official Qt documentation (doc/html/mainwindows-application.html).

It is a simple application, built around the QPlainTextEdit, a QtGui widget very similar to QTextEdit, but specifically aimed at the plain text management.

The project consists of a handful of files: mainwindow.h and .cpp for the QMainWindow subclass; main.cpp for the C++ main; application.qrc, a file containing the resource associated to the project: half a dozen images used for the buttons in the toolbar.

The application is so simple that misses a few useful standard features, but other examples are provided for them.

I suggest you to get the code, compile it, and get familiar with it, playing a bit around with it.

Let's have a close look at the code.

As we said, almost all the code in the application is in the MainWindow class, the QMainWindow subclass.

Its constructor create the QPlainTextEdit widget, stores it in the member textEdit and assigns it to the window central widget:

textEdit = new QPlainTextEdit();
setCentralWidget(textEdit);

Then calls a bunch of setup functions (more about them later), and makes an interesting connection:

connect(textEdit->document(), SIGNAL(contentsChanged()),
this, SLOT(documentWasModified()));

After this call, the signal contentsChanged() of the textEdit document is connected with the documentWasModified() slot of WinMain.

That means that any time the text is changed in the document available to the user, a call to documentWasModified() is performed:

void WinMain::documentWasModified()
{
setWindowModified(textEdit->document()->isModified());
}

In this way we keep the local windowModified flag aligned with the modified flag for the document. And this is going to reflect in the window title, where a star will be put (for Windows) near the file name when the window is marked as modified.

Then we call setCurrentFile() passing an empty string. If we have a look at this function we find out that:

void WinMain::setCurrentFile(const QString &fileName)
{
curFile = fileName; // 1.
textEdit->document()->setModified(false);
setWindowModified(false);

QString shownName = curFile; // 2.
if (curFile.isEmpty())
shownName = "untitled.txt";
setWindowFilePath(shownName);
}

1. the passed filename, in this case the empty string, is stored in the data member curFile, then the modify flag is set to false for both the document and the window.
2. the windows name is changed to show the name of the current file - in case of a new file (like here, in the constructor) a default name is showed instead.

Last line in the constructor, we call a specific method for Mac, setUnifiedTitleAndToolBarOnMac(), I didn't check what it is about, since I'm working on a windows machine.

Let see now the four setup function called by the constructor.

createActions(): as we can imaging from its name, it is about creating the actions associated with the menu items.

The first action is newAct, used to create a new file, and this is the related code:

newAct = new QAction(QIcon(":/images/new.png"), "&New", this); // 1.
newAct->setShortcuts(QKeySequence::New);
newAct->setStatusTip("Create a new file");
connect(newAct, SIGNAL(triggered()), this, SLOT(newFile())); // 2.

1. we create the action, passing an icon file name, the name of the action as showed to the user, and the owner of the action, responsible for its deletion. Notice that the icon file name starts with a colon (:) that means it is a resource path. We'll talk about it later.
2. through this connection, when the action is triggered, the relative function is executed. In this case the slot is newFile()

The rest of the code of createActions() is, more or less, all the same. There's a variation just at its end:

cutAct->setEnabled(false);
copyAct->setEnabled(false);
connect(textEdit, SIGNAL(copyAvailable(bool)), cutAct, SLOT(setEnabled(bool)));
connect(textEdit, SIGNAL(copyAvailable(bool)), copyAct, SLOT(setEnabled(bool)));

A couple of actions are marked as disabled - and this would reflect on the button associated to these actions, being showed as grayed.
Moreover the signal copyAvailable(bool) for the textEdit is connected with the slot setEnabled(bool) for both the action cut and copy. That means that these to actions will be active only when the text has some available for the copy.

createMenus(): it uses the actions created in createActions() to create the menu for the main window.

The structure is fairly easy, let's have a look a the first two lines of this function:

fileMenu = menuBar()->addMenu("&File");
fileMenu->addAction(newAct);

After creating a menu, delegating the job to the menu bar, we add an action that is displayed as an item in the menu.

createToolBars(): it's quite easy to add tool bars to a window, using the addToolBar() window method.

Here is the first two lines of this function, where we see how to create a tool bar, and how to add an action to it:

fileToolBar = addToolBar("File");
fileToolBar->addAction(newAct);


createStatusBar(): in a window, the status bar is always there, the fact is it is not showed. So creating it it's just a matter of displaying test in it, and that is what if done in this mono-line function:

statusBar()->showMessage("Ready");

Actually, if we had nothing to display, and we would just want to show the empty status bar, we could have call clearMessage().

Usually an application has a few values that we want to persist. And that is done here by the to functions readSettings() and writeSettings().

Another interesting point, is how to connect the application to a resource file. This is done in the main file for the application, with this line:
Q_INIT_RESOURCE(application);

That look for the application.qrc file. We should also put the qrc file name in the qt project (.pro) file, in the resources section.

No comments:

Post a Comment