QT: работа с файлом, настройки приложения и файл ресурсов...
... и всё в одном проекте хотелось бы, так что примера лучше текстового редактора всё равно не придумаешь. Пример будем делать в актуальном QT 5.2.1, а редактор пока будет однооконным как "Блокнот".
Создадим виджет на основе QMainWindow
, встроенная пустая форма нам не нужна, поэтому просто перепишем файл проекта (который с расширением .pro
) в виде
QT += widgets HEADERS = mainwindow.h SOURCES = main.cpp \ mainwindow.cpp RESOURCES = application.qrc
а файл mainwindow.ui
можно убить. Последней строкой мы сказали, что у нашего приложения будут ещё и ресурсы. Не интересовался, как их добавить через графический интерфейс Qt Creator (наверное, можно), мне проще создать в папке проекта любым редактором текста файл application.qrc
и написать там:
<!DOCTYPE RCC><RCC version="1.0"> <qresource> <file>images/copy.png</file> <file>images/cut.png</file> <file>images/new.png</file> <file>images/open.png</file> <file>images/paste.png</file> <file>images/save.png</file> </qresource> </RCC>
...а в папке проекта создать вложенную папку images
с картинками, имена которых заданы в XML-файле. Это будут картинки и для пунктов меню, и для графических кнопок.
Главная программа main.cpp
будет парой строк отличаться от стандарта, я написал комментарием, для чего могут понадобиться добавленные строки:
#include <QApplication> #include "mainwindow.h" int main(int argc, char *argv[]) { Q_INIT_RESOURCE(application); QApplication app(argc, argv); QString myOrganization = "My organization"; QString myApplicationName = "Text Editor Example"; //Имена нашей "организации" и "приложения" - потом пригодятся при //сохранении/восстановлении настроек программы app.setOrganizationName(myOrganization); app.setApplicationName(myApplicationName); MainWindow mainWindow(myOrganization,myApplicationName); mainWindow.show(); return app.exec(); }
Файл mainwindow.h
приведу без комментариев, там нет ничего особенного, просто описаны:
- прототипы конструкторов (понадобился всего один);
- прототипы функций, реализующих виртуальные методы класса
QWidget
(нам понадобится тоже один - обработчик закрытия окнаcloseEvent
); - публичные слоты для обработки команд приложения;
- приватные методы и элементы интерфейса приложения.
Вот полный текст файла:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> class QAction; class QMenu; class QPlainTextEdit; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QString myOrganization, QString myApplicationName); protected: void closeEvent(QCloseEvent *event); //Обработка события закрытия приложения private slots: void newFile(); void open(); bool save(); bool saveAs(); void about(); void documentWasModified(); private: void createActions(); void createMenus(); void createToolBars(); void createStatusBar(); void readSettings(); void writeSettings(); bool maybeSave(); void loadFile(const QString &fileName); bool saveFile(const QString &fileName); void setCurrentFile(const QString &fileName); QString strippedName(const QString &fullFileName); QPlainTextEdit *textEdit; QString curFile; QMenu *fileMenu; QMenu *editMenu; QMenu *helpMenu; QToolBar *fileToolBar; QToolBar *editToolBar; QAction *newAct; QAction *openAct; QAction *saveAct; QAction *saveAsAct; QAction *exitAct; QAction *cutAct; QAction *copyAct; QAction *pasteAct; QAction *aboutAct; QAction *aboutQtAct; QString myName, myOrganization, myApplicationName; }; #endif
Всё остальное будет делать mainwindow.cpp
. Я включил комментарии прямо в текст программы, внимательно читая его, Вы легко найдёте ответы на поставленные в заголовке вопросы.
#include <QtWidgets> #include "mainwindow.h" MainWindow::MainWindow(QString myOrganization, QString myApplicationName) //Конструктор главного окна виджета { myName = "Текстовый редактор"; //Именуем приложение this->myOrganization = myOrganization; this->myApplicationName = myApplicationName; textEdit = new QPlainTextEdit; //Основное окно редактирования setCentralWidget(textEdit); createActions(); //Для разгрузки конструктора создадим действия (команды меню), createMenus(); //главное меню, createToolBars(); //кнопочные панели инструментов и createStatusBar(); //строку состояния в отдельных функциях readSettings(); //Прочитать установки приложения connect(textEdit->document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); //определили слот для отслежиивания изменений в документе setCurrentFile(""); //Установить текущий файл, в начале - пусто setUnifiedTitleAndToolBarOnMac(true); //Только в QT5.2 и выше - совместимость с Mac OS } void MainWindow::createActions() //Создадим действия для пунктов меню { newAct = new QAction(QIcon(":/images/new.png"), tr("&Новый"), this); //Параметры конструктора QAction: иконка из ресурсов, текст меню, родитель newAct->setShortcuts(QKeySequence::New); //В классе QKeySequence уже определены стандартные комбинации клавиш newAct->setStatusTip(tr("Создать новый файл")); //Подсказка для строки состояния; //tr() - макрос, который может пригодиться //при автоматической локализации приложения connect(newAct, SIGNAL(triggered()), this, SLOT(newFile())); //Назначили слот для обработки выбора пункта меню openAct = new QAction(QIcon(":/images/open.png"), tr("&Открыть..."), this); openAct->setShortcuts(QKeySequence::Open); openAct->setStatusTip(tr("Открыть существующий файл")); connect(openAct, SIGNAL(triggered()), this, SLOT(open())); saveAct = new QAction(QIcon(":/images/save.png"), tr("&Сохранить"), this); saveAct->setShortcuts(QKeySequence::Save); saveAct->setStatusTip(tr("Сохранить документ на диск")); connect(saveAct, SIGNAL(triggered()), this, SLOT(save())); saveAsAct = new QAction(tr("Сохранить к&ак..."), this); //Более простой конструктор QAction: текст меню, родитель saveAsAct->setShortcuts(QKeySequence::SaveAs); saveAsAct->setStatusTip(tr("Сохранить копию документа под новым именем")); connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs())); exitAct = new QAction(tr("В&ыход"), this); exitAct->setShortcuts(QKeySequence::Quit); exitAct->setStatusTip(tr("Закрыть приложение")); connect(exitAct, SIGNAL(triggered()), this, SLOT(close())); cutAct = new QAction(QIcon(":/images/cut.png"), tr("&Вырезать"), this); cutAct->setShortcuts(QKeySequence::Cut); cutAct->setStatusTip(tr("Переместить выделенный фрагмент в Буфер Обмена")); connect(cutAct, SIGNAL(triggered()), textEdit, SLOT(cut())); copyAct = new QAction(QIcon(":/images/copy.png"), tr("&Копировать"), this); copyAct->setShortcuts(QKeySequence::Copy); copyAct->setStatusTip(tr("Скопировать выделенный фрагмент в Буфер Обмена")); connect(copyAct, SIGNAL(triggered()), textEdit, SLOT(copy())); pasteAct = new QAction(QIcon(":/images/paste.png"), tr("Вст&авить"), this); pasteAct->setShortcuts(QKeySequence::Paste); pasteAct->setStatusTip( tr("Вставить в текущее место документа содержимое Буфера Обмена")); connect(pasteAct, SIGNAL(triggered()), textEdit, SLOT(paste())); aboutAct = new QAction(tr("&О программе"), this); aboutAct->setStatusTip(tr("Информация о приложении")); connect(aboutAct, SIGNAL(triggered()), this, SLOT(about())); aboutQtAct = new QAction(tr("О &Qt"), this); aboutQtAct->setStatusTip(tr("Информация о библиотеке Qt")); connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt())); //qApp - глобальный указатель на экземпляр приложения //у QWidget уже есть стандартный слот aboutQt() cutAct->setEnabled(false); //Некоторые пункты меню сначала недоступны copyAct->setEnabled(false); connect(textEdit, SIGNAL(copyAvailable(bool)), cutAct, SLOT(setEnabled(bool))); connect(textEdit, SIGNAL(copyAvailable(bool)), copyAct, SLOT(setEnabled(bool))); //Слоты для управления состоянием этих пунктов } void MainWindow::createMenus() //Создание главного меню { fileMenu = menuBar()->addMenu(tr("&Файл")); //В QMainWindow уже есть menuBar, просто добавим туда меню fileMenu->addAction(newAct); fileMenu->addAction(openAct); fileMenu->addAction(saveAct); fileMenu->addAction(saveAsAct); fileMenu->addSeparator(); fileMenu->addAction(exitAct); editMenu = menuBar()->addMenu(tr("&Правка")); editMenu->addAction(cutAct); editMenu->addAction(copyAct); editMenu->addAction(pasteAct); menuBar()->addSeparator(); helpMenu = menuBar()->addMenu(tr("&Справка")); helpMenu->addAction(aboutAct); helpMenu->addAction(aboutQtAct); } void MainWindow::createToolBars() //Создание кнопочных тулбаров { fileToolBar = addToolBar(tr("File")); fileToolBar->addAction(newAct); fileToolBar->addAction(openAct); fileToolBar->addAction(saveAct); editToolBar = addToolBar(tr("Edit")); editToolBar->addAction(cutAct); editToolBar->addAction(copyAct); editToolBar->addAction(pasteAct); //Добавили команды, для которых определили QIcon } void MainWindow::createStatusBar() //Создание строки состояния { statusBar()->showMessage(tr("Готово")); } bool MainWindow::maybeSave() //Есть ли несохранённыеи изменения? { if (textEdit->document()->isModified()) { QMessageBox::StandardButton ret; ret = QMessageBox::warning(this, tr("Приложение"), tr("В документе есть несохранённые изменения.\n" "Хотите сохранить документ?"), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); if (ret == QMessageBox::Save) return save(); else if (ret == QMessageBox::Cancel) return false; } return true; } void MainWindow::setCurrentFile(const QString &fileName) //Управляет отображением имени текущего файла { curFile = fileName; textEdit->document()->setModified(false); setWindowModified(false); QString shownName = curFile; if (curFile.isEmpty()) shownName = "untitled.txt"; setWindowFilePath(shownName); //В QWidget уже есть метод для связи пути к файлу с виджетом //setWindowTitle(strippedName(shownName)); //лишнее } QString MainWindow::strippedName(const QString &fullFileName) //Вернёт имя файла без пути { return QFileInfo(fullFileName).fileName(); } void MainWindow::closeEvent(QCloseEvent *event) //Обработчик закрытия приложения { if (maybeSave()) { //Если нужно сохранять, writeSettings(); //то сохранить и event->accept(); //подтвердить событие закрытия } else { event->ignore(); //иначе отменить обработку } } void MainWindow::newFile() //Создаёт новый файл { if (maybeSave()) { textEdit->clear(); setCurrentFile(""); } } void MainWindow::open() //Открывает существующий файл { if (maybeSave()) { QString fileName = QFileDialog::getOpenFileName(this); if (!fileName.isEmpty()) loadFile(fileName); } } bool MainWindow::save() //Обработка пункта меню "Сохранить" { if (curFile.isEmpty()) { return saveAs(); } else { return saveFile(curFile); } } bool MainWindow::saveAs() //Обработка пункта меню "Сохранить как" { QString fileName = QFileDialog::getSaveFileName(this); if (fileName.isEmpty()) return false; return saveFile(fileName); } bool MainWindow::saveFile(const QString &fileName) //Непосредственно сохранение файла { QFile file(fileName); //Свяжем файловую переменную с именем файла if (!file.open(QFile::WriteOnly | QFile::Text)) { QMessageBox::warning(this, tr("%1").arg(myName), tr("Не могу сохранить файл %1\nПричина: %2.") .arg(fileName) .arg(file.errorString())); return false; } QTextStream out(&file); //...и будем сохранять через поток... //...и, если надо, управлять курсором (менять на "песочные часы") #ifndef QT_NO_CURSOR QApplication::setOverrideCursor(Qt::WaitCursor); #endif out << textEdit->toPlainText(); //Само сохранение #ifndef QT_NO_CURSOR QApplication::restoreOverrideCursor(); #endif setCurrentFile(fileName); statusBar()->showMessage(tr("Файл сохранён"), 2000); //Покажем сообщение в строке статуса на 2 секунды return true; } void MainWindow::about() //Выводит окно "О программе" { QMessageBox::about(this, tr("О программе"), tr("Это просто <b>%1</b>, показывающий, " "как сделать на QT современную программу с графическим интерфейсом, " "используя меню, панели инструментов и строку состояния.").arg(myName)); } void MainWindow::documentWasModified() //Ставит метку модификации главного окна { setWindowModified(textEdit->document()->isModified()); } void MainWindow::readSettings() //Читает настройки приложения { QSettings settings(myOrganization, myApplicationName); //Параметры конструктора QSettings: имя организации, имя приложения //Класс обеспечивает кросс-платрофменное сохранение настроек. //Для Windows - в реестре, для Unix-платформ с KDE будет создан ini-файл, //для Mac OS - XML-файл QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); //ключ, значение по умолчанию QSize size = settings.value("size", QSize(400, 400)).toSize(); resize(size); move(pos); } void MainWindow::writeSettings() //Запоминает настройки приложения { QSettings settings(myOrganization, myApplicationName); settings.setValue("pos", pos()); settings.setValue("size", size()); //Просто будем помнить позицию и размер окна } void MainWindow::loadFile(const QString &fileName) //Открытие файла { QFile file(fileName); if (!file.open(QFile::ReadOnly | QFile::Text)) { QMessageBox::warning(this, tr("%1").arg(myName), tr("Не могу открыть файл %1\nПричина: %2.") .arg(fileName) .arg(file.errorString())); return; } QTextStream in(&file); #ifndef QT_NO_CURSOR QApplication::setOverrideCursor(Qt::WaitCursor); #endif textEdit->setPlainText(in.readAll()); #ifndef QT_NO_CURSOR QApplication::restoreOverrideCursor(); #endif setCurrentFile(fileName); statusBar()->showMessage(tr("Файл открыт"), 2000); }
Вот какое красивое приложение у нас вышло:
Окно приложения QT "Текстовый редактор с меню и кнопками"
Скачать архив .zip с проектом QT 5 "Текстовый редактор и меню" (15 Кб)
08.05.2014, 19:39 [24894 просмотра]