Рис. 3.9. «Сохранить изменения?»
Вызов функции warning() на первый взгляд может показаться слишком сложным, но он имеет очень простой формат:
QMessageBox::warning(родительский объект, заголовок, сообщение, кнопка0, кнопка1, …);
QMessageBox содержит функции information(), question() и critical(), каждая из которых имеет собственную пиктограмму.
Рис. 3.10. Пиктограммы окна сообщения.
01 void MainWindow::open()
02 {
03 if (okToContinue()) {
04 QString fileName =
QFileDialog::getOpenFileName(".", fileFilters, this);
05 if (!fileName.isEmpty())
06 loadFile(fileName);
07 }
08 }
Слот open() соответствует пункту меню File | Open. Как и слот newFile(), он сначала вызывает okToContinue() для обработки несохраненных изменений. Затем он вызывает удобную статическую функцию QFileDialog::getOpenFileName() для получения от пользователя нового имени файла. Эта функция выводит на экран диалоговое окно для выбора пользователем файла и возвращает имя файла или пустую строку при нажатии пользователем клавиши Cancel.
В первом аргументе функции QFileDialog::getOpenFileName() задается родительский виджет. Взаимодействие родительских и дочерних объектов для диалоговых окон и для других виджетов будет различно. Диалоговое окно всегда является самостоятельным окном, однако если у него имеется родитель, то оно размещается по умолчанию в верхней части родительского объекта. Кроме того, дочернее диалоговое окно использует панель задач родительского объекта.
Во втором аргументе задается название диалогового окна. В третьем аргументе задается каталог начала просмотра файлов; в нашем случае это будет текущий каталог.
Четвертый аргумент определяет фильтры файлов. Фильтр файла состоит из описательной части и образца поиска. Если допустить поддержку не только родного формата файлов приложения Электронная таблица, а также формата файлов с запятой в качестве разделителя и файлов Lotus 1-2-3, нам пришлось бы инициализировать переменные следующим образом:
tr("Spreadsheet files (*.sp)\n"
"Comma-separated values files (*.csv)\n"
"Lotus 1-2-3 files (*.wk1 *.wks)")
Закрытая функция loadFile() вызвана в open() для загрузки файла. Мы делаем эту функцию независимой, поскольку нам потребуется выполнить те же действия для загрузки файлов, которые открывались недавно:
01 bool MainWindow::loadFile(const QString &fileName)
02 {
03 if (!spreadsheet->readFile(fileName)) {
04 statusBar()->showMessage(tr("Loading canceled"), 2000);
05 return false;
06 }
07 setCurrentFile(fileName);
08 statusBar()->showMessage(tr("File loaded"), 2000);
09 return true;
10 }
Мы используем функцию Spreadsheet::readFile() для чтения файла с диска. Если загрузка завершилась успешно, мы вызываем функцию setCurrentFile() для обновления заголовка окна; в противном случае функция Spreadsheet::readFile() уведомит пользователя о возникшей проблеме, выдав соответствующее сообщение. В целом полезно предусматривать выдачу сообщений об ошибках в компонентах низкого уровня, поскольку они могут обеспечить получение точной информации о причинах ошибки.
В обоих случаях мы будем выдавать сообщение в строке состояния в течение 2 секунд (2000 миллисекунд) для того, чтобы пользователь знал о выполняемых приложением действиях.
01 bool MainWindow::save()
02 {
03 if (curFile.isEmpty()) {
04 return saveAs();
05 } else {
06 return saveFile(curFile);
07 }
08 }
09 bool MainWindow::saveFile(const QString &fileName)
10 {
11 if (!spreadsheet->writeFile(fileName)) {
12 statusBar()->showMessage(tr("Saving canceled"), 2000);
13 return false;
14 }
15 setCurrentFile(fileName);
16 statusBar()->showMessage(tr("File saved"), 2000);
17 return true;
18 }
Слот save() соответствует пункту меню File | Save. Если файл уже имеет имя, потому что уже открывался до этого или уже сохранялся, слот save() вызывает saveFile(), задавая это имя; в противном случае он просто вызывает saveAs().