Опубликован: 07.03.2015 | Уровень: для всех | Доступ: свободно | ВУЗ: Компания ALT Linux

Лекция 15: Разработка приложений с графическим интерфейсом

15.4 Стандартные диалоги

Диалог выбора файла для открытия и сохранения, диалог выбора шрифта, окна сообщений об ошибках являются примерами диалоговых окон, с которыми часто приходится сталкиваться при работе с программами. Такие диалоги обычно имеют стандартные для всех программ в системе вид и функциональность. Qt позволяет воспользоваться готовыми диалогами для этих целей, которые легко вызвать в программе. Классы для работы с диалогами, которые часто используют в программе, приведены в табл. 15.1.

Таблица 15.1. Некоторые классы готовых диалогов Qt
Класс Описание особенностей
QInputDialog Используют для удобства в случае когда необходимо ввести числовое значение или строку текста. Имеет несколько сигналов, которые сигнализируют об изменении значения в поле ввода. Класс имеет статические методы для вызова диалога ввода числа (getDouble(), getInt()), ввода (getText()) выбора элемента из списка (getItem()).
QColorDialog Стандартный диалог выбoра цвета. Класс имеет статический метод getColor() для удобного вызова диалога.
QFontDialog Стандартный диалог выбoра шрифта. Класс имеет статический метод getFont() для удобного вызова диалога.
QFileDialog Стандартный диалог выбора файла. Имеет большое количество настроек, возможность фильтрации файлов по расширению. Класс имеет статические методы (getExistingDirectory(), getOpenFileName(), getOpenFileNames(), getSaveFileName()) для вызова диалога
QMessageBox Диалог сообщение. Используют для вывода информации, сообщений об ошибках и вопросов. Класс имеет статические методы для удобного вызова в программе информационных окон (about(), aboutQt()), сообщений об ошибках (critical(), warning()), вопросов (question()) и сообщений (information()).

В нашем примере мы будем использовать класс QFileDialog для выбора файла при открытии и сохранении, а также класс QMessageBox. Добавим описание слотов для открытия и сохранения файла:

private slots :
void slotOpen ( );
void slotSave ( );

Подключим необходимые заголовочные файлы в mainwindow.cpp:

#include <QFileDialog>
#include <QMessageBox>
#include <QDir>

Добавим реализацию для слотов slotOpen() и slotSave():

//Слот для открытия файла в редакторе
void MainWindow::slotOpen ( )
{
	//Вызвать системный диалог открытия файла в домашней папке пользователя
	QString lFileName=QFileDialog::getOpenFileName ( this, " open file...", QDir : :
	homePath ( ), " Text files ( * .txt );; All files ( *.* ) " );
	//указываем фильтры для просмотра файлов
	if ( lFileName.isEmpty ( ) ) //Если пользователь не выбрал ни одного файла
	{
		return; //выйти из метода
	}
	//Спросить пользователя о сохранении документа
	if ( ! askForFileSaveAndclose ( ) )
	{
	//Если пользователь нажал "Отмена" игнорировать вызов — продолжать работу
		return;
	}
	QFile lFile ( lFileName ); //Устанавливаем имя открытого файла
	//Если текстовый файл открыт только для чтения...
	if ( lFile.open ( QIODevice::ReadOnly | QIODevice::Text ) )
	{
		mFileName = lFileName; //задать имя файла
		//читаем все содержимое и устанавливаем текст для редактора
		ui->plainTextEdit ->setPlainText( lFile.readAll ( ) );
		lFile.close ( ); //закрываем открытый файл
		//устанавливаем состояние окна — содержимое не модифицировано
		setWindowModified ( false );
		//и обновляем заголовок окна для демонстрации названия текущего открытого файла
		updateTitle ( );
	}
	else
	{
		//Если при открытии файла возникла ошибка выводим диалоговое окно с сообщением,
		//содержащим имя файла, одну кнопку "Ок" и заголовок "Error"
		QMessageBox::warning ( this, " Error ", QString ( " Could not open File %1 for
		reading " ).arg ( lFile.FileName ( ) ), QMessageBox::Ok);
	}
}
//Слот для сохранения изменений в текущем файле
void MainWindow::slotsave ( )
{
	//Если содержимое не модифицировано...
	if ( ! isWindowModified ( ) ) //Если содержимое не модифицировано
	{
	return; //Выйти из метода — продолжить работу
	}
	//Вызвать системный диалог сохранения файла в домашней папке пользователя
	QString lFileName=QFileDialog::getSaveFileName ( this, t r ( " Save File..." ),
		QDir::homePath ( ), tr ( " Text files ( *.txt );; All files ( *.* ) " ) );
	//Если пользователь не выбрал имя файла для сохранения...
	if ( lFileName.isEmpty ( ) )
	{
		return; // ... выйти из метода
	}
	QFile lFile ( lFileName ); //Устанавливаем имя открытого файла
	//Если текстовый файл открыт для записи
	if ( lFile.open ( QIODevice::WriteOnly | QIODevice::Text ) )
	{
		mFileName = lFileName; //Задать имя файла
		//Создаем временный QByteArray для записи данных
		QByteArray lData;
		//Читаем текст из редактора и добавляем QByteArray, записываем в файл
		//и закрываем файл после записи
		lData.append ( ui->plainTextEdit ->toPlainText( ) );
		lFile.write ( lData );
		lFile.close ( );
		//Устанавливаем состояние окна — содержимое не модифицировано
		setWindowModified ( false );
	}
	else
	{
		//Если при открытии файла возникла ошибка выводим диалоговое окно с сообщением,
		//содержащим имя файла, одну кнопку "Ок" и заголовок "Error"
		QMessageBox::warning ( this, " Error ", QString ( " Could not open File %1 for
		writing " ).arg ( lFile.FileName ( ) ), QMessageBox::Ok);
	}
}

Здесь в начале каждого из слотов мы вызываем диалог для выбора файла с помощью статических методов класса QFileDialog. Диалог для открытия файла мы вызываем с помощью метода QFileDialog::getOpenFileName(), а для сохранения — с помощью QFileDialog ::getSaveFileName(). Для каждого из случаев диалог будет иметь соответствующий вид. Для того, чтобы добавить фильтр для текстовых файлов мы передаем строку с описанием фильтра — "Text files (*.txt );;AllFiles (*.*)". В описании — названия для фильтров и шаблоны для фильтрации (в скобках). Можно задавать несколько шаблонов через пробел при необходимости. Фильтры в списке разделяем с помощью двойной точки с запятой.

После выбора пользователем файла, статический метод вернет полный путь к нему. В случае, когда пользователь закрыл диалог или нажал "Отмена", метод вернет пустую строку. Для выбранного файла мы выполняем чтение содержимого и сохранения. Если при открытии возникла ошибка, мы выводим ее с помощью статического метода QMessageBox::warning().

При открытии файла мы использовали собственный метод askForFileSaveAndClose (), который должен проверить текущий открытый файл на изменения и предложить пользователю сохранить их перед открытием другого файла. Добавим описание этого метода к описанию класса MainWindow:

private :
	bool askForFileSaveAndclose ( );

и его реализацию в файл mainwindow.cpp:

//Метод для проверки текущего файла на изменения и вывода диалога для пользователя,
//с предложением сохранить изменения. Метод возвращает логическое значение,
//содержащее false в случае, когда пользователь нажал в диалоге кнопку "Cancel"
bool MainWindow::askForFileSaveAndclose ( )
{
	if ( isWindowMod ified ( ) ) //Если содержимое окна модифицировано
	{
	//вызываем диалог с вопросом, нужно ли сохранять изменения: подставляем в текст диалога
	//название текущего открытого файла, задаем кнопки: "Да", "Нет" и "Отмена".
	//Результат работы диалога (нажатой кнопки) записываем в переменную
		int lResult = QMessageBox::question ( this, t r ( " Save changes " ),
		QString ( tr ( " File %1 is modified.Do you want to save your changes ? " ) ).arg
			( mFile.FileName ( ) ), QMessageBox::Yes, QMessageBox::No, QMessageBox : :
			Cancel );
		if ( QMessageBox::Yes == lResult ) //Если нажали кнопку "Да"
		{
			slotSave ( ); //сохранить изменения
		}
		else
		{
			if ( QMessageBox::Cancel == lResult ) //Если нажали кнопку "Отменить"
			{
				return false;
			}
		}
	}
	return true;
}

В этом фрагменте программы мы использовали статический метод QMessageBox :: question, и задали заголовок, текст и кнопки на диалоге с помощью специальных констант (QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel). Он, как и почти все статические методы QMessageBox, возвращает значение нажатой кнопки. Это значение мы сравниваем со значениями констант для кнопок, чтобы определить, какие дальнейшие действия выбрал пользователь. Используем метод askForFileSaveAndClose() также и в слоте для нового текстового файла:

//Спросить пользователя о сохранении документа
if ( ! askForFileSaveAndclose ( ) )
{
	//Если пользоватеель нажал "Отменить" игнорировать вызов — продолжать работу
	return;
}

Осталось реализовать вывод информации о программе. Для этого сначала добавим к .pro-файлу информацию о версии. Например, добавим переменные с большим и меньшим номерами версии, а потом передадим их в переменную DEFINES таким образом, чтобы они были объявлены в программе. Это может быть удобно для дальнейшего изменения версии при разработке программы:

MAJOR_VERSION = 1
MINOR_VERSION = 0
DEFINES += \
	MAJOR_VERSION=$$MAJOR_VERSION \
	MINOR_VERSION=$$MINOR_VERSION

После изменений в файле проекта не забудьте вызвать qmake еще раз, чтобы программа обработала все изменения в файле проекта (выберите в главном меню Build->Run qmake) Теперь в файле main.cpp зададим версию, а также название для QApplication:

int main ( int arg c, char * argv [ ] )
{
	QApplication a ( arg c, argv );
	a.setApplicationName ( " TextEditor " );
	a.setApplicationVersion ( QString ( " % 1.% 2 " )
		. arg (MAJOR_VERSION)
		. arg (MINOR_VERSION) );
	MainWindow w;
	w.show ( );
	return a.exec ( );
}

Теперь используем объект QApplication для получения версии и названия программы в слоте отображения информации. Добавим объявления слота, а также следующую его реализацию:

//Слот для отображения информации о программе
void MainWindow::slotAboutProgram ( )
{
	//Выводим диалоговое информационное окно с сообщением, куда подставляем версию и название
	//программы возвращаемых QApplication. Указываем — окно содержит заголовок "About".
	QMessageBox::about ( this, t r ( " About " ),
	QString ( " %1 v.%2 " ).arg ( qApp->
	ApplicationName ( ) ).arg ( qApp->Application Version ( ) ) );
}

В конце подсоединяем сигналы от пунктов главного меню к созданным слотам:

//Присоединяем действия к созданным слотам
connect ( ui->action_New, SIGNAL( triggered ( ) ), this, SLOT( slotNew ( ) ), Qt : :
	UniqueConnection );
connect ( ui->action_Open, SIGNAL( triggered ( ) ), this, SLOT( slotOpen ( ) ), Qt : :
	UniqueConnection );
connect ( ui->act ion_Save, SIGNAL( triggered ( ) ), this, SLOT( slotsave ( ) ), Qt : :
	UniqueConnection );
connect ( ui->actionAbout_Qt, SIGNAL( triggered ( ) ), qApp, SLOT( aboutQt ( ) ), Qt : :
	UniqueConnection );
connect ( ui->actionAbout_program, SIGNAL( triggered ( ) ), this, SLOT(
slotAboutProgram ( ) ), Qt::UniqueConnection );
Сергей Радыгин
Сергей Радыгин

Символы кириллицы выводит некорректно. Как сделать чтобы выводился читабельный текст на русском языке?

Тип приложения - не Qt,

Qt Creator 4.5.0 основан на Qt 5.10.0. Win7.

 

Юрий Герко
Юрий Герко

Кому удалось собрать пример из раздела 13.2 Компоновка (Layouts)? Если создавать проект по изложенному алгоритму, автоматически не создается  файл mainwindow.cpp. Если создавать этот файл вручную и добавлять в проект, сборка не получается - компилятор сообщает об отсутствии класса MainWindow. Как правильно выполнить пример?