Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум на CrossPlatform.RU _ Qt Ввод/Вывод, Сеть. Межпроцессное взаимодействие _ Как закрыть файл при закрытии приложения

Автор: silver47 5.4.2011, 9:35

Приложение пишет лог в файл. Необходимо чтобы при закрытии приложения, собственно, этот лог сохранился. Не могу отловить событие закрытия

QObject::connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(appClose()));
. Деструктор тоже не выполняется. Может кто может помочь?

Спасибо.

Автор: abc 5.4.2011, 9:54

окну нужно присвоить флаг deleteOnClose, вроде так пишется, тогда деструктор выполнится
ещё
closeEvent - у твоего главного окна есть такая виртуальная функция
а ещё стоит сохранять лог периодически, потому что программа может завершиться аварийно
не вижу ничего плохого в том, чтобы с каждой записью открывать и закрывать файл

Автор: silver47 5.4.2011, 10:30

Цитата(abc @ 5.4.2011, 11:54) *
окну нужно присвоить флаг deleteOnClose, вроде так пишется, тогда деструктор выполнится
ещё
closeEvent - у твоего главного окна есть такая виртуальная функция
а ещё стоит сохранять лог периодически, потому что программа может завершиться аварийно
не вижу ничего плохого в том, чтобы с каждой записью открывать и закрывать файл


По поводу того, чтобы с каждой записью открывать и закрывать файл, сам только подумал и переделал уже. А вот про deleteOnClose, как присвоить его окну?

Автор: wiz29 5.4.2011, 11:02

QWidget* pWidget(new QWidget);
pWidget->setAttribute (Qt::WA_DeleteOnClose, true);

раз у тебя оконное приложение попробуй обработать сигнал от QApplication : void lastWindowClosed ().

Автор: silver47 5.4.2011, 11:03

Цитата(wiz29 @ 5.4.2011, 13:02) *
QWidget* pWidget(new QWidget);
pWidget->setAttribute (Qt::WA_DeleteOnClose, true);

раз у тебя оконное приложение попробуй обработать сигнал от QApplication : void lastWindowClosed ().


У меня оно не наследуется от QWidget. У меня нет окон - нечего закрывать.

Пока сделал так, чтобы файл с логом был всегда закрыт. открывается лишь на незначительное время для добавления записи, для решения задачи этого вполне достаточно. Но может все-же можно как-то понять, что твое приложение закрыли тем или иным образом? Вроде встречал на каком-то форуме, что платформонезависимо нельзя, но это касается только консольных приложений.

Автор: wiz29 5.4.2011, 11:29

можешь линкануть код функции main?

просто в любом случае одной обработкой сигнала от QCoreApplication нельзя отделаться.

Автор: silver47 5.4.2011, 11:39

Цитата(wiz29 @ 5.4.2011, 13:29) *
можешь линкануть код функции main?


main, в моем случае не делает ничего, кроме создания экземпляра класса и запуска лупа.
#include <QtCore/QCoreApplication>
#include <QTextCodec>
#include "mainapp.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    //***************************************************************
    QTextCodec *codec=QTextCodec::codecForName("UTF8");
    QTextCodec::setCodecForLocale(codec);
    QTextCodec::setCodecForCStrings(codec);
    //***************************************************************

    MainApp  aplication;

    QObject::connect(&aplication, SIGNAL(connectFailed()), &a, SLOT(quit()));

    return a.exec();
}

Автор: wiz29 5.4.2011, 11:50

объект "logger" синглтон в приложении?

если это так то все просто нужно немного модифицировать код твоего main и все будет в "шоколаде".
строку return a.exec(); поменяй на 2:

 int ret = a.exec();

 //закрыть логер

и вначале обязательно сделай переопределение стандартного обработчика "terminate handler function", это поможет тебе при завершении логера в случае непредвиденного завершения приложения. инфу можно посмотреть тут http://www.cplusplus.com/reference/std/exception/set_terminate/

Автор: silver47 5.4.2011, 12:02

Цитата(wiz29 @ 5.4.2011, 13:50) *
объект "logger" синглтон в приложении?

если это так то все просто нужно немного модифицировать код твоего main и все будет в "шоколаде".
строку return a.exec(); поменяй на 2:
 int ret = a.exec();

 //закрыть логер

и вначале обязательно сделай переопределение стандартного обработчика "terminate handler function", это поможет тебе при завершении логера в случае непредвиденного завершения приложения. инфу можно посмотреть тут http://www.cplusplus.com/reference/std/exception/set_terminate/


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

Автор: abc 5.4.2011, 12:02

Wiz,
имеешь ввиду?

int res = a.exec();
file.close();
return res;

про terminate только на днях прочёл, оказывается, вот где можно использовать)

Автор: wiz29 5.4.2011, 12:22

Цитата(silver47 @ 5.4.2011, 12:02) *
Простите. Совсем не понял. Объект "логгер" пишет в файл события, которые происходят при обработке данных другими классами, они кидают ему их посредством сигналов. Другие классы должны выполняться в лупе, то есть всегда, это не парсер чего-либо. Если они выполнили свою работу они уничтожаются, родительский класс создает новый объект, как только работа появиться. Надеюсь понятно объяснил.

Невижу никаких проблем с тем чтобы ваш "логгер" был глобальным в приложении (даже если он потомок QObject) или же у вас есть некий общий файл в который все "сливается" с разных объектов (т.е. разделенный доступ к лог файлу)? (тогда здесь не все понятно с синхронизацией доступа к файлу).
p.s. Что конкретно не понятно? (синглтон - объект одиночка для всего приложения, в qt например, это QCoreApplication объект, как правило имеет метод instance() или нечто подобное)

кстати обработка сигнала aboutToQuit нормально работает только при корректном завершении приложения: вызовом метода QCoreApplication::instance()->quit(); в остальных случая код завершения не 0 и сигнал не генерится.

Автор: silver47 5.4.2011, 12:41

Логгер глобальный и один для всех объектов.

Конкретно не понятно как работает set_terminate.
По примеру мы устанавливаем метод, который будет вызываться в случае ненормального завершения программы. Это так? зачем тогда заменять a.exec(); на int res = a.exec();?

Если сделать все чисто по примеру, то вроде как не работает. Добавил консоль в сборку:

#include <QtCore/QCoreApplication>
#include <iostream>
#include <QTextCodec>
#include "mainapp.h"

void abnormalExit(){
    std::cout << "Abnormal exit";
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    //***************************************************************
    QTextCodec *codec=QTextCodec::codecForName("UTF8");
    QTextCodec::setCodecForLocale(codec);
    QTextCodec::setCodecForCStrings(codec);
    //***************************************************************

    MainApp  aplication;
    QObject::connect(&aplication, SIGNAL(connectFailed()), &a, SLOT(quit()));

    std::set_terminate(abnormalExit);
    return a.exec();
}


По Ctrl+C тихо закрывается. Ничего не оставляет после себя. Если вставить throw; до return a.exec() то исключение вызывает метод abnormalExit.

Автор: wiz29 5.4.2011, 13:26

1. менять не обязательно.
2. std::set_terminate работает следующим образом: если в какойто части программы возникает исключительная ситуация exception и данная ситуация не обработана ни одним перехватчиком искл. ситуаций то приложение аварийно завершается, перед своим завершением вызываться некая специальная глобальная функция terminate handler , собственно указатель на нее и устанавливает данный метод (это коротко , на "палцах") за более подробной инфой обратитесь к справочникам по C++. При закрытии консольного окна "через крестик" реально сам не смог отловить завершение.

Средставми Qt закрытие консоли с помощью Сtr + C или Alt + F4 скорее всего обработать не выйдет, придется писать платформозависимый код. (других путей пока не вижу)

Автор: Алексей1153 5.4.2011, 17:05

вообще говоря, достаточно просто всегда записывать данные в файл. Когда приложение умирает, система закрывает все дескрипторы, занятые приложением - файл автоматом закроется. По крайней мере в винде так :)

Автор: abc 5.4.2011, 19:33

Алексей, что происходит здесь?
http://www.forum.crossplatform.ru/index.php?showtopic=6720

Автор: Алексей1153 5.4.2011, 19:42

abc,

ну да, если что-то не успело записаться, то оно и не сохранится. А по топику в ссылке, навскидку:

вызывать flush после каждой записи - это гарантия перекидывания в файл из внутренних буферов. А close там ни при чём ИМХО

опять же, тонкости работы линукса я не знаю, а в винде файл просто закроется и ничего не потеряется из уже в него записанного :)

Автор: abc 5.4.2011, 20:06

я в винде компилировал, QString не пишется, QPixmap пишется... Как может не успеть записаться? дальше этих двух типов не проверял

Автор: Алексей1153 5.4.2011, 20:17

abc, а с flush тоже не пишется ?

Автор: abc 5.4.2011, 20:20

не пробовал, но если даже пишется, то вопрос изменится на - почему QString записать нужен flush, а QPixmap нет... :)

Автор: Алексей1153 5.4.2011, 20:25

abc, возможно, оператор << у них по разному устроен - в одном flush вызывается, в другом нет

Автор: abc 5.4.2011, 20:36

хмм, ладно, надо провести более качественные проверки для продолжения беседы)

Автор: silver47 6.4.2011, 6:05

Цитата(Алексей1153 @ 5.4.2011, 22:17) *
abc, а с flush тоже не пишется ?


В случае, если используется QTextStream для вывода в файл, то flush бесполезен.
void QTextStream::flush ()
Flushes any buffered data waiting to be written to the device.
If QTextStream operates on a string, this function does nothing.

Если писать в файл напрямую, не используя QTextStream, то QFile::flush решает проблему. Я ее все-же предпочел решить по другому - держать файл закрытым (в лог сыплются лишь сообщения об ошибках), теперь вопрос в другом, так сказать для общего развития: Как узнать что твое приложения прибили тем или иным способом? Как я понимаю платформонезависимо никак.

Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)