Solved Why can't QSettings be written to QTemporaryFile
-
@Pl45m4 Hi, thanks!
The temporary file has indeed been successfully created. I can see it in the folder.Debug output:
JCDemoAES::test 1 "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug/debug/JCDemoAES.LBTGmC"
JCDemoAES::test 2 "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug/debug/JCDemoAES.LBTGmC"
JCDemoAES::test 3 ""void JCDemoAES::test() { // Init temp file tmpFile = new QTemporaryFile(QStringLiteral("%1/%2").arg(QApplication::applicationDirPath()).arg(QApplication::applicationName()), this); tmpFile->open(); qDebug() << __FUNCTION__ << 1 << tmpFile->fileName(); tmpFile->close(); // Settings QSettings settings(tmpFile->fileName(), QSettings::IniFormat); qDebug() << __FUNCTION__ << 2 << settings.fileName(); 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.sync(); settings.endGroup(); // Read temp file tmpFile->open(); qDebug() << __FUNCTION__ << 3 << tmpFile->readAll(); tmpFile->close(); }
-
QSettings::sync() should go after QSettings::endGroup().
-
@jeremy_k Thank you for your patience!
Debug output:
JCDemoAES::test 1 "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug/debug/JCDemoAES.zJpgBl"
JCDemoAES::test 2 "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug/debug/JCDemoAES.zJpgBl"
JCDemoAES::test 3 "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug/debug/JCDemoAES.zJpgBl"
JCDemoAES::test 4 ""void JCDemoAES::test() { // Init temp file tmpFile = new QTemporaryFile(QStringLiteral("%1/%2").arg(QApplication::applicationDirPath()).arg(QApplication::applicationName()), this); tmpFile->open(); qDebug() << __FUNCTION__ << 1 << tmpFile->fileName(); tmpFile->close(); // Settings QSettings settings(tmpFile->fileName(), QSettings::IniFormat); qDebug() << __FUNCTION__ << 2 << settings.fileName(); 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 tmpFile->open(); qDebug() << __FUNCTION__ << 3 << tmpFile->fileName(); qDebug() << __FUNCTION__ << 4 << tmpFile->readAll(); tmpFile->close(); }
-
There could be an issue because
InitFormat
may expect an*.ini
extension. Your temp file seem to have ini file format, but a random file extension.
It could be possible thatQSettings
wont recognize this file. But this is just a guess.
The read/write access looks good nowOn Unix, NativeFormat and IniFormat mean the same thing, except that the file extension is different (.conf for NativeFormat, .ini for IniFormat).
-
@Pl45m4 I tested QFile, the write failure should not be caused by the extension.
Debug output:
JCDemoAES::test 1 "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug/debug/JCDemoAES.TBjIps"
JCDemoAES::test 2 "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug/debug/JCDemoAES.TBjIps"
JCDemoAES::test 3 "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug/debug/JCDemoAES.TBjIps"
JCDemoAES::test 4 "[GroupTest]\nUserCode=0000, 1111, 2222, 3333\nUserCodeDefault=1234\n"void JCDemoAES::test() { // Init temp file // tmpFile = new QTemporaryFile(QStringLiteral("%1/%2").arg(QApplication::applicationDirPath()).arg(QApplication::applicationName()), this); tmpFile = new QFile(QStringLiteral("%1/%2.TBjIps").arg(QApplication::applicationDirPath()).arg(QApplication::applicationName()), this); tmpFile->open(QIODevice::ReadWrite | QIODevice::Text); qDebug() << __FUNCTION__ << 1 << tmpFile->fileName(); tmpFile->close(); // Settings QSettings settings(tmpFile->fileName(), QSettings::IniFormat); qDebug() << __FUNCTION__ << 2 << settings.fileName(); 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 tmpFile->open(QIODevice::ReadWrite | QIODevice::Text); qDebug() << __FUNCTION__ << 3 << tmpFile->fileName(); qDebug() << __FUNCTION__ << 4 << tmpFile->readAll(); tmpFile->close(); }
-
Got (actually not me) the solution...
Try to open the tempFile one time BEFORE you create the
QSettings
object.
Then it should work :)Here's the MVP :)
https://stackoverflow.com/a/57471942fileName
returns an empty string when the file is not open. Which you did in your first try (but wrong sync). In your latest approach, the file is closed when creating the settings.
It's aQTemporaryFile
thing, this is why it worked withQFile
even when the file is closed.So
open
before settings andsync
afterendGroup
:) -
This works for me:
#include <QCoreApplication> #include <QSettings> #include <QTemporaryFile> #include <QTextCodec> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QTemporaryFile tmpFile("/tmp/tmpfile.XXXXXXX"); 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:
21:19:24: Starting /tmp/build-settingsfile-Desktop_Qt_5_15_5_clang_64bit-Debug/settingsfile ... file "/tmp/tmpfile.ZlFULTY" content "[General]\nUserCode=test value\n"
-
The issue seems to be caching in the QIODevice, at least on macOS with Qt 5.15.5.
#include <QCoreApplication> #include <QSettings> #include <QTemporaryFile> #include <QTextCodec> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QTemporaryFile tmpFile("/tmp/tmpfile.XXXXXXX"); 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 "/tmp/tmpfile.NmlyxbW" "/tmp/tmpfile.NmlyxbW" content "[General]\nUserCode=test value\n" tmpfile "some text"
-
https://doc.qt.io/qt-6/qiodevice.html says:
Some subclasses, such as QFile and QTcpSocket, are implemented using a memory buffer for intermediate storing of data.My guess is that closing and reopening the QTemporaryFile doesn't discard its buffer and fails to notice the underlying file change.
-
@Pl45m4
Output:
JCDemoAES::test 1 "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug/debug/JCDemoAES.NYWrOp"
JCDemoAES::test 2 "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug/debug/JCDemoAES.NYWrOp"
JCDemoAES::test 3 "E:/JCShared/Projects/JCDemo/JCDemoAES/build-JCDemoAES-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug/debug/JCDemoAES.NYWrOp"
JCDemoAES::test 4 ""void JCDemoAES::test() { // Init temp file tmpFile = new QTemporaryFile(QStringLiteral("%1/%2").arg(QApplication::applicationDirPath()).arg(QApplication::applicationName()), this); tmpFile->open(); qDebug() << __FUNCTION__ << 1 << tmpFile->fileName(); // Settings QSettings settings(tmpFile->fileName(), QSettings::IniFormat); qDebug() << __FUNCTION__ << 2 << settings.fileName(); 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 qDebug() << __FUNCTION__ << 3 << tmpFile->fileName(); qDebug() << __FUNCTION__ << 4 << tmpFile->readAll(); tmpFile->close(); }
-
@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.