Solved Why can't QSettings be written to QTemporaryFile
-
@jeremy_k
PC:
Win10 64bit
Qt 5.14.2QTemporaryFile tmpFile(QStringLiteral("%1/%2").arg(QApplication::applicationDirPath()).arg(QApplication::applicationName())); tmpFile.open(); tmpFile.close(); QSettings settings(tmpFile.fileName(), QSettings::IniFormat); settings.setIniCodec(QTextCodec::codecForName("UTF-8")); settings.setValue("UserCode", "test value"); settings.sync(); QFile reader(tmpFile.fileName()); reader.open(QIODevice::ReadOnly); qDebug() << "file" << tmpFile.fileName(); qDebug() << "content" << reader.readAll();
Output:
file "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug/debug/JCDemoAES.IwayTr"
content "" -
@jeremy_k said in Why can't QSettings be written to QTemporaryFile:
e seems to be caching in the QIODev
QTemporaryFile tmpFile(QStringLiteral("%1/%2").arg(QApplication::applicationDirPath()).arg(QApplication::applicationName())); tmpFile.open(); tmpFile.write("some text"); tmpFile.seek(0); qDebug() << "tmpfile" << tmpFile.readAll(); tmpFile.close(); QSettings settings(tmpFile.fileName(), QSettings::IniFormat); settings.setIniCodec(QTextCodec::codecForName("UTF-8")); settings.setValue("UserCode", "test value"); settings.sync(); QFile reader(tmpFile.fileName()); reader.open(QIODevice::ReadOnly); qDebug() << "file" << tmpFile.fileName() << reader.fileName(); qDebug() << "content" << reader.readAll(); tmpFile.open(); qDebug() << "tmpfile" << tmpFile.readAll();
Output:
tmpfile "some text"
file "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug/debug/JCDemoAES.bveDgi" "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug/debug/JCDemoAES.bveDgi"
content "some text"
tmpfile "some text" -
I tested qt5.15.2 and couldn't work.
PC: Win10 64-bit
Compiler: MSVC2019 -
You should check QSettings::status() and will notice that you get a QSettings::FormatError since you try to load an invalid ini file.
qDebug() << "Settings status: " << settings.status();
-
@Christian-Ehrlicher Hi, thanks.
It's "QSettings::NoError".
Output:
JCDemoAES::test 1 "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug/debug/JCDemoAES.tSHxeC"
JCDemoAES::test 2 QSettings::NoError
JCDemoAES::test file: "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug/debug/JCDemoAES.tSHxeC"
JCDemoAES::test content: ""// Init temp file QTemporaryFile tmpFile(QStringLiteral("%1/%2").arg(QApplication::applicationDirPath(), QApplication::applicationName()), this); tmpFile.open(); tmpFile.close(); qDebug() << __FUNCTION__ << 1 << tmpFile.fileName(); // Settings QSettings settings(tmpFile.fileName(), QSettings::IniFormat); qDebug() << __FUNCTION__ << 2 << settings.status(); settings.setIniCodec(QTextCodec::codecForName("UTF-8")); settings.beginGroup("GroupTest"); QStringList list; list << QStringLiteral("0000") << QStringLiteral("1111") << QStringLiteral("2222") << QStringLiteral("3333"); settings.setValue("UserCode", list); settings.setValue("UserCodeDefault", QStringLiteral("1234")); settings.endGroup(); settings.sync(); // Read temp file QFile reader(tmpFile.fileName()); reader.open(QIODevice::ReadOnly); qDebug() << __FUNCTION__ << "file:" << tmpFile.fileName(); qDebug() << __FUNCTION__ << "content:" << reader.readAll();
-
@Christian-Ehrlicher
QSettings::status() is "AccessError" after settings.sync();
Output:
JCDemoAES::test 3 QSettings::AccessErrorsettings.endGroup(); settings.sync(); qDebug() << __FUNCTION__ << 3 << settings.status();
-
I don't know why access error occurred.
AccessError
public static final QSettings.Status AccessErrorAn access error occurred (e.g. trying to write to a read-only file).
A QTemporaryFile will always be opened in QIODevice::ReadWrite mode, this allows easy access to the data in the file. This function will return true upon success and will set the fileName() to the unique filename used.
-
Output:
JCDemoAES::test 1 "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug/debug/JCDemoAES.lwKvTB"
JCDemoAES::test 2 QSettings::NoError
JCDemoAES::test 3 QSettings::AccessError
JCDemoAES::test file: "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug/debug/JCDemoAES.lwKvTB"
JCDemoAES::test content: ""// Init temp file tmpFile = new QTemporaryFile(QStringLiteral("%1/%2").arg(QApplication::applicationDirPath(), QApplication::applicationName()), this); tmpFile->open(); qDebug() << __FUNCTION__ << 1 << tmpFile->fileName(); // Settings QSettings settings(tmpFile->fileName(), QSettings::IniFormat); qDebug() << __FUNCTION__ << 2 << settings.status(); settings.setIniCodec(QTextCodec::codecForName("UTF-8")); settings.beginGroup("GroupTest"); QStringList list; list << QStringLiteral("0000") << QStringLiteral("1111") << QStringLiteral("2222") << QStringLiteral("3333"); settings.setValue("UserCode", list); settings.setValue("UserCodeDefault", QStringLiteral("1234")); settings.endGroup(); settings.sync(); qDebug() << __FUNCTION__ << 3 << settings.status(); // Read temp file QFile reader(tmpFile->fileName()); reader.open(QIODevice::ReadOnly); qDebug() << __FUNCTION__ << "file:" << tmpFile->fileName(); qDebug() << __FUNCTION__ << "content:" << reader.readAll(); // Close temp file tmpFile->close();
-
@tovax
https://doc.qt.io/qt-5/qtemporaryfile.html#detailsReopening a QTemporaryFile after calling close() is safe. For as long as the QTemporaryFile object itself is not destroyed, the unique temporary file will exist and be kept open internally by QTemporaryFile.
It is possible that the fact the file is kept open internally interferes with
QSettings
ability to write to the file. Create or handQSettings
your own filename in the same directory asQTemporaryFile
would (QDir::tempPath()
) and verify that succeeds.Reduce your code to minimum. Remove the
setIniCodec()
, and get rid of thebeginGroup()
stuff and theQStringList
, just write a single simple value while you test. -
You can't open a file twice for read/write as @JonB correctly told you.
-
@tovax
I suspect you will find there is indeed a problem trying to use aQTemporaryFile
forQSettings
, for whatever reason. I refer you to https://www.mail-archive.com/interest@qt-project.org/msg32865.htmlI decided to look at writing the settings out to a QTemporaryFile,
then just reading that data back in as a string, and then let the
QTemporaryFile go out of scope and clean itself up. But according to the
following test code, QSettings does not seem to play nicely with QTemporaryFile:
I suggest you read that thread and look at his code. Read through all the responses (I did not.) I think you will find the final advice is to use your own temporary file instead.
-
@JonB I don't see why it's needed - all what is needs to be done is written here. Create a QTemporaryFile, open it, close it again, pass filename to QSettings and go ahead.
-
@Christian-Ehrlicher said in Why can't QSettings be written to QTemporaryFile:
Create a QTemporaryFile, open it, close it again, pass filename to QSettings and go ahead.
I believe the OP here tried that earlier (his post before last above) but got the same "access error", did he not? See his earlier code above:
QTemporaryFile tmpFile(QStringLiteral("%1/%2").arg(QApplication::applicationDirPath(), QApplication::applicationName()), this); tmpFile.open(); tmpFile.close();
And probably that reference tried to.
QTemporaryFile::close()
does not close the file, reference the docs I quoted earlier, and I looked at the source and all it does itseek(0)
. Doubtless to do with keeping a handle into it until theQTemporaryFile
goes out of scope, whereupon it will be closed and deleted. Hence if the fact that it is kept open (for all I know with exclusive access) is what stopsQSettings
working cannot be avoided. -
@JonB You're right. It can't work since completely closing a QTemporaryFile will delete it (as this is the usecase for this class).
I also don't see a reason to use a QTemporaryFile here at all. -
@tovax might have better luck using QTemporaryDir, and then creating a QFile with a known name within.
-
Here is what the docs say:
Reopening a QTemporaryFile after calling close() is safe. For as long as the QTemporaryFile object itself is not destroyed, the unique temporary file will exist and be kept open internally by QTemporaryFile.
So, the temporary file is kept open which is why QSettings cannot write to it. In the end, you just need a temporary file name. How about this:
QString tmpFileName; { QTemporaryFile tmpFile(QStringLiteral("%1/%2").arg(QApplication::applicationDirPath(), QApplication::applicationName()), this); tmpFile.open(); tmpFileName = tmpFile.fileName(); } ...
Note the curly braces to restrict the scope if of the temporary file (so it will actually be closed). I cringed a little when I saw that you leaked a pointer to the temporary file because you used
new
, but neverdelete
d it. -
@SimonSchroeder said in Why can't QSettings be written to QTemporaryFile:
So, the temporary file is kept open which is why QSettings cannot write to it. In the end, you just need a temporary file name. How about this:
That is what I quoted/wrote earlier on.
I do not understand why you think your code will help the OP. It is true that while the
QTemporaryFile tmpFile
is in scope Qt holds an open handle into it, which seems to stopQSettings
writing to it. However I have previously quoted from the docs, maybe you did not notice, https://doc.qt.io/qt-5/qtemporaryfile.html#detailsand the file will subsequently be removed upon destruction of the QTemporaryFile object.
The whole point is that when it goes out of scope to release the handle it also deletes the file. OP cannot write to file within the scope and the file will not exist any longer out of scope.
@tovax
As I sad earlier. Do not useQTemporaryFile
here. Use static QString QDir::tempPath() to make your own temporary file and use that instead. It's not hard to make sure you delete that file explicitly when you are done with it. -
@SimonSchroeder Hi, thank you very much for your reply. I think the tmpFile will be deleted out of the curly braces.
-
@Christian-Ehrlicher @jeremy_k @JonB @Pl45m4 @SimonSchroeder Thank you very much! QTemporaryDir works.
Output:
1 "temp dir:" "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug/debug/.hsxxlg"
2 "temp file:" "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug/debug/.hsxxlg/JCDemoAES.tmp"
3 "settings status:" QSettings::NoError
4 "temp file content:" [GroupTest]
UserCode=0000, 1111, 2222, 3333
UserCodeDefault=1234// Temp dir QTemporaryDir tmpDir(QStringLiteral("%1/").arg(QApplication::applicationDirPath())); qDebug() << 1 << QString("temp dir:") << tmpDir.path(); // Temp file QFile tmpFile(QStringLiteral("%1/%2.tmp").arg(tmpDir.path(), QApplication::applicationName()), this); tmpFile.open(QFile::ReadWrite | QFile::Text | QFile::Truncate); tmpFile.close(); qDebug() << 2 << QString("temp file:") << tmpFile.fileName(); // Settings QSettings settings(tmpFile.fileName(), QSettings::IniFormat); settings.setIniCodec(QTextCodec::codecForName("UTF-8")); settings.beginGroup("GroupTest"); QStringList list; list << QStringLiteral("0000") << QStringLiteral("1111") << QStringLiteral("2222") << QStringLiteral("3333"); settings.setValue("UserCode", list); settings.setValue("UserCodeDefault", QStringLiteral("1234")); settings.endGroup(); settings.sync(); // very important qDebug() << 3 << QString("settings status:") << settings.status(); // Read temp file tmpFile.open(QFile::ReadOnly | QFile::Text); QByteArray ba = tmpFile.readAll(); qDebug() << 4 << QString("temp file content:") << ba.data(); tmpFile.close();
-
Reopening a QTemporaryFile after calling close() is safe. For as long as the QTemporaryFile object itself is not destroyed, the unique temporary file will exist and be kept open internally by QTemporaryFile.