=> Главная База Знаний Qt Управление сеансами в системе x11


Управление сеансами в системе x11

Управление сеансами в системе x11

Когда мы выходим из системы X11, некоторые оконные менеджеры спрашивают нас о необходимости сохранения сеанса. Если мы отвечаем утвердительно, то при следующем входе в систему работа приложений будет автоматически возобновлена с того же экрана и, в идеальном случае, с того же состояния, которое было во время выхода из системы.

Компонент системы X11, который обеспечивает сохранение и восстановление сеанса, называется менеджером сеансов (session manager). Для того чтобы приложение Qt/X11 «осознавало» присутствие менеджера сеансов, мы должны переопределить функцию QApplication::saveState() и сохранить там состояние приложения.

Рис. 20.7. Выход из системы KDE.

Windows 2000 и XP, а также некоторые системы Unix предлагают другой механизм, который носит название «спящих процессов» (hibernation). Когда пользователь останавливает компьютер, операционная система просто выгружает оперативную память компьютера на диск и загружает ее обратно, когда компьютер «просыпается». Приложениям ничего не надо делать, и они даже могут ничего не знать об этом.

Когда пользователь инициирует завершение работы, мы можем перехватить управление непосредственно перед завершением путем переопределения функции QApplication::commitData(). Это позволяет нам сохранять измененные данные и при необходимости вступать в диалог с пользователем. Эта часть схемы управления сеансом поддерживается как в системе X11, так и в Windows.

Мы рассмотрим управление сеансом через программный код приложения Tic-Tac-Toe (крестики-нолики), которое работает под управлением менеджера сеансов. Во-первых, давайте рассмотрим функцию main():

01 int main(int argc, char *argv[])

02 {

03 Application app(argc, argv);

04 TicTacToe toe;

05 toe.setObjectName("toe");

06 app.setTicTacToe(&toe);

07 toe.show();

08 return app.exec();

09 }

Мы создаем объект Application. Класс Application наследует QApplication и переопределяет две функции commitData() и saveState() для обеспечения управления сеансом.

Затем мы создаем виджет TicTacToe, даем знать об этом объекту Application и отображаем его. Мы дали виджету TicTacToe имя «toe». Мы должны давать уникальные имена виджетам верхнего уровня, если мы хотим, чтобы менеджер сеансов мог восстановить размеры и позиции окон.

Рис. 20.8. Приложение Tic-Tac-Toe.

Ниже приводится определение класса Application:

01 class Application : public QApplication

02 {

03 Q_OBJECT

04 public:

05 Application(int &argc, char *argv[]);

06 void setTicTacToe(TicTacToe *tic);

07 void saveState(QSessionManager &sessionManager);

08 void commitData(QSessionManager &sessionManager);

09 private:

10 TicTacToe *ticTacToe;

11 };

Класс Application сохраняет указатель виджета TicTacToe в закрытой переменной.

01 void Application::saveState(QSessionManager &sessionManager)

02 {

03 QString fileName = ticTacToe->saveState();

04 QStringList discardCommand;

05 discardCommand << "rm" << fileName;

06 sessionManager.setDiscardCommand(discardCommand);

07 }

В системе X11 функция saveState() вызывается, когда менеджер сеансов собирается сохранить состояние приложения. Данная функция также имеется на других платформах, но никогда не вызывается. Параметр QSessionManager позволяет нам поддерживать связь с менеджером сеансов.

Мы начинаем с попытки сохранения виджетом TicTacToe своего состояния в некоторый файл. Затем мы задаем команду для выполнения сброса состояния менеджером сеансов. Команда сброса (discard command) — это команда, которую должен выполнять менеджер сеансов для удаления любой сохраненной ранее информации, связанной с текущим состоянием. В этом примере мы задаем ее в виде

rm файл_сеанса

где файл_сеанса — имя файла, который содержит сохраненное состояние сеанса, a rm — стандартная команда удаления файлов в системе Unix.

Менеджер сеансов имеет также команду рестарта (restart command). Эту команду менеджер сеансов должен выполнять для возобновления работы приложения. По умолчанию Qt обеспечивает следующую команду рестарта:

приложение -session идентификатор_ключ

Первая часть, приложение, извлекается из argv[0]. Идентификатор — это идентификатор сеанса, переданный менеджером сеансов; гарантированно обеспечивается его уникальность для различных приложений и различных сеансов работы одного приложения. Ключ добавляется для однозначной идентификации времени сохранения сеанса. По различным причинам менеджер сеансов может вызывать функцию saveState() несколько раз в одном сеансе, и различные состояния должны отличаться.

Из-за ограничений существующих менеджеров сеансов нам необходимо убедиться, что каталог приложения содержится в переменной среды PATH, если мы хотим обеспечить правильный рестарт приложения. В частности, если вы сами собираетесь попробовать пример TicTacToe, вы должны установить его в каталог, например, /usr/bin и вызывать его по команде tictactoe.

Для простых приложений, в том числе и для TicTacToe, мы могли бы для обеспечения команды рестарта сохранять состояние в дополнительном аргументе командной строки. Например:

tictactoe -state 0X-X0-X-0

Это избавило бы нас от сохранения данных в файле и выдачи команды сброса состояния для удаления файла.

01 void Application::commitData(QSessionManager &sessionManager)

02 {

03 if (ticTacToe->gameInProgress()

04 && sessionManager.allowsInteraction()) {

05 int r = QMessageBox::warning(ticTacToe, tr("Tic-Tac-Toe"),

06 tr("The game hasn't finished.\n"

07 "Do you really want to quit?"),

08 QMessageBox::Yes | QMessageBox::Default,

09 QMessageBox::No | QMessageBox::Escape);

10 if (г == QMessageBox::Yes) {

11 sessionManager.release();

12 } else {

13 sessionManager.cancel();

14 }

15 }

16 }

Функция commitData() вызывается, когда пользователь выходит из системы. Мы можем переопределить ее для вывода сообщения, предупреждающего пользователя о потенциальной потере данных. В используемой по умолчанию реализации закрываются все виджеты верхнего уровня, что равносильно ситуации, когда пользователь последовательно закрывает все окна, нажимая кнопку закрытия в заголовках окон. В главе 3 мы показали, как можно переопределять функцию closeEvent(), перехватывающую этот момент и выводящую на экран сообщение.

Рис. 20.9. «Вы действительно хотите завершить работу?».

Теперь давайте рассмотрим класс TicTacToe:

01 class TicTacToe : public QWidget

02 {

03 Q_OBJECT

04 public:

05 TicTacToe(QWidget *parent = 0);

06 bool gameInProgress() const;

07 QString saveState() const;

08 QSize sizeHint() const;


09 protected:

10 void paintEvent(QPaintEvent *event);

11 void mousePressEvent(QMouseEvent *event);


12 private:

13 enum { Empty = '-', Cross = 'X', Nought = '0' };


14 void clearBoard();

15 void restoreState();

16 QString sessionFileName() const;

17 QRect cellRect(int row, int column) const;

18 int cellWidth() const { return width() / 3; }

19 int cellHeight() const { return height() / 3; }

20 bool threeInARow(int row1, int col1, int row3, int col3) const;


21 char board[3][3];

22 int turnNumber;

23 };

Класс TicTacToe наследует QWidget и переопределяет функции sizeHint(), paintEvent() и mousePressEvent(). Он также обеспечивает функции gameInProgress() и saveState(), которые мы использовали в нашем классе Application.

01 TicTacToe::TicTacToe(QWidget *parent, const char *name)

02 : QWidget(parent, name)

03 {

04 clearBoard();

05 if (qApp->isSessionRestored())

06 restoreState();

07 setWindowTitle(tr("Tic-Tac-Toe"));

08 }

В конструкторе мы стираем игровое поле и, если приложение было вызвано с опцией —session, вызываем закрытую функцию restoreState() для восстановления старого сеанса.

01 void TicTacToe::clearBoard()

02 {

03 for (int row= 0; row < 3; ++row) {

04 for (int column = 0; column < 3; ++column) {

05 board[row][column] = Empty;

06 }

07 }

08 turnNumber = 0;

09 }

В функции clearBoard() мы стираем все ячейки и устанавливаем turnNumber на значение 0.

01 QString TicTacToe::saveState() const

02 {

03 QFile file(sessionFileName());

04 if (file.open(QIODevice::WriteOnly)) {

05 QTextStream out(&file);

06 for (int row = 0; row < 3; ++row) {

07 for (int column = 0; column < 3; ++column) {

08 out << board[row][column];

09 }

10 }

11 }

12 return file.fileName();

13 }

В функции saveState() мы записываем состояние игрового поля на диск. Формат достаточно простой: «X» для крестиков, «0» для ноликов и «—» для пустых ячеек.

01 QString TicTacToe::sessionFileName() const

02 {

03 return QDir::homePath() + "/.tictactoe_"

04 + qApp->sessionId() + "_" + qApp->sessionKey();

05 }

Закрытая функция sessionFileName() возвращает имя файла для текущего идентификатора сеанса и ключа сеанса. Данная функция используется как в saveState(), так и в restoreState(). Имя файла определяется на основе идентификатора сеанса и ключа сеанса.

01 void TicTacToe::restoreState()

02 {

03 QFile file(sessionFileName());

04 if (file.open(QIODevice::ReadOnly)) {

05 QTextStream in(&file);

06 for (int row = 0; row < 3; ++row) {

07 for (int column = 0; column < 3; ++column) {

08 in >> board[row][column];

09 if (board[row][column] != Empty)

10 ++turnNumber;

11 }

12 }

13 }

14 update();

15 }

В функции restoreState() мы загружаем файл восстанавливаемого сеанса и заполняем игровое поле его информацией. Мы рассчитываем значение переменной turnNumber исходя из количества крестиков и ноликов на игровом поле.

В конструкторе TicTacToe мы вызывали restoreState(), если функция QApplication::isSessionRestored() возвращала true. В этом случае sessionId() и sessionKey() возвращают именно те значения, которые были при прошлом сохранении состояния приложения, а функция sessionFileName() возвращает имя файла того сеанса.

Тестирование и отладка программного кода по управлению сеансами могут быть достаточно утомительным делом, поскольку нам приходится все время входить и выходить из системы. Один из способов, позволяющий избежать этого, заключается в применении стандартной утилиты xsm, предусмотренной в системе X11. При первом вызове xsm на экран выводятся окно менеджера сеансов и окно консольного режима. Все приложения, запускаемые с данного окна консольного режима, будут использовать xsm в качестве своего менеджера сеансов, а не стандартный общесистемный менеджер сеансов. Мы можем затем использовать окно xsm для завершения, рестарта или сброса сеанса и проконтролировать правильность поведения приложения. Подробное описание того, как это делается, вы найдете в сети Интернет по адресу http://doc.trolltech.com/4.1/session.html.