Global QSettings object created from IniFormat and UserScope - c++

I am trying to use a QSettings object with an IniFormat for UserScope settings loaded at the start of the application. I moved the QSettings setup code into a separate method and call it from main() as shown in this snippet:
#include <QDebug>
#include <QSettings>
#include <QStringList>
void loadSettings()
{
qDebug() << "[BEGIN] loadSettings()";
QCoreApplication::setOrganizationName("Org");
QCoreApplication::setApplicationName("App");
QSettings settings(QSettings::IniFormat,
QSettings::UserScope,
"Org",
"App");
settings.setValue("TheAnswer", "42");
QStringList keys = settings.allKeys();
qDebug() << "Loaded " << keys.size() << " keys.";
qDebug() << "[END] loadSettings()";
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
loadSettings();
QSettings settings;
QStringList keys = settings.allKeys();
qDebug() << "Settings has " << keys.size() << " keys.";
// Empty
qDebug() << settings.value("TheAnswer").toString();
return a.exec();
}
The resulting output is:
[BEGIN] loadSettings()
Loaded 1 keys.
[END] loadSettings()
Settings has 0 keys.
""
Looking at the documentation for QSettings, it states that the usage of QCoreApplication to set the organization name and application name would allow the usage of the convenience QSettings creation method from anywhere in the application, so my understanding is that the code snippet should be able to access the value stored with key "TheAnswer" that was loaded by the loadSettings() method. Yet, when I create a new QSettings object using the convenience method, it has no key/value pairs. I verified the ini file is created and has correct data. What am I missing?

I believe the problme is that default format is QSettings::NativeFormat instead of QSettings::IniFormat, which you are using.
I noticed that there's a static QSettings::setDefaultFormat() function, so I would try adding this to your loadSettings() function:
QSettings::setDefaultformat( QSettings::IniFormat );
Also, once you've set the application/organization and default format, I don't think you need to pass any arguments to the QSettings constructor in your loadSettings() function.

Related

QProcess input string with accents

I have two programs each with QProcess and I have a different behavior concerning the QProcess input with accentuated characters
(more precisely I create a Qprocess to execute a dos copy command and the path takes accent).
The environnement of execution and development is Windows 10.
The first program is a simple prototype that I made to test if my code can work correctly.
Here is the prototype code I have, in which the copy works correctly, integrated in a simple main() function.
The code is supposed to copy a file named sfx.exe into a path with accent F:\path_accentué and it indeed does the copy correctly.
#include <QCoreApplication>
#include <Qdebug>
#include <QObject>
#include <QProcess>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QProcess* processus = new QProcess();
QStringList args;
QString path("F:\\path_accentué");
args << "/C" << "copy" << "E:\\test\\sfx.exe" << path;
processus->start("cmd.exe", args);
if (!processus->waitForStarted())
{
qDebug() << "Could not launch the process";
}
//processus->write(s.c_str());
if (!processus->waitForFinished(-1))
{
qDebug() << "Finished";
}
delete processus;
return app.exec();
}
However, when I integrate (literally copies and pasted without changing) this prototype within a bigger code project, my instance of QProcess does not recognize the accentuated path, as if accents are no more supported.
This is the part that I copy/paste in the bigger project and which I now execute via a button click in QT.
And this time, the QProcess doesn't recognize the accentuated path (Instead it creates a file with name like this path_accentu�)
QProcess* processus = new QProcess();
QStringList args;
QString path("F:\\path_accentué");
args << "/C" << "copy" << "E:\\test\\sfx.exe" << path; processus->start("cmd.exe", args);
if (!processus->waitForStarted())
{
qDebug() << "Could not launch the process";
}
//processus->write(s.c_str());
if (!processus->waitForFinished(-1))
{
qDebug() << "Finished";
}
I do not find in the documentation a way to force the QProcess to recognize accentuated inputs.
I would like to understand why the QProcess instance behaves differently when integrated within my bigger project.
What may impact the behavior of the QProcess and leads it to treat differently the input in the second case?
Note:
The QProcess is needed for more things but not only for the copy (such as getting feedback and percentage of operations). The copy is only to isolate the problem. In reality, I do much more things.
I tried to recreate your behaviour with Qt 5.15 and could create a file with accent with
start("cmd",{args...})
start("cmd /c args...")
setNativeArguments("/c args...") + start("cmd")
Last one is recommended for "cmd" calls, see the remarks here:
https://doc.qt.io/qt-5/qprocess.html#start
The only thing, which did not work, because it deadlocks was
setArguments({args...}) + start("cmd")
Demo here:
https://gist.github.com/elsamuko/59f110cf3a678beae9db27860f6305c9

In qt QFileDialog setsuffix is not working in linux, how to solve?

I am working on a Save dialog for my qt app. Everything works, but if no file extension is added behind the filename, it won't automatically be saved with the file extension although the filter is selected.
I know i need to set a defaultsuffix option, but even if i do, then it still won't add the extension automatically if its not given.
I found several other similar questions, where i read it works in windows but it could fail on linux distro's. If so, is there a simple workaround? Because right now, i don't have a working solution...
void MainWindow::on_actionSave_Chart_As_triggered()
{
QFileDialog *fileDialog = new QFileDialog;
fileDialog->setDefaultSuffix("files (*);;AstroQt aqt (*.aqt)");
QString fileName = fileDialog->getSaveFileName(this, "Save Radix", ui->label_2->text() +".aqt", "AstroQT(*.aqt)");
qDebug() << " save file name " << fileName << endl;
QFile file(fileName);
if (!file.open(QFile::WriteOnly | QFile::Text)) {
QMessageBox::warning(this, "Warning", "Cannot save file: " + file.errorString());
return;
}
setWindowTitle(fileName);
QTextStream out(&file);
QString text = "text that will be saved...";
out << text;
file.close();
}
Edit: After trying multiple solutions, none seemed to work. But it should have, i guess. Why else is there a aftersuffix function...? For now i solved it doing it manually. But i'm not happy with it, there should be a better solution/explanation.
// add extension if none is found.
if(!fileName.endsWith(".aqt"))
fileName.append(".aqt");
If you use the static method getSaveFileName things seems to work correctly:
#include <QFileDialog>
#include <QApplication>
#include <QDebug>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QString fileName = QFileDialog::getSaveFileName(
nullptr, QObject::tr("Save File"),
"teste.aqt",
QObject::tr("AstroQt (*.aqt)"));
qDebug() << " save file name " << fileName << endl;
return app.exec();
}
I get the correct file name with the extension, if I type something without the extension.
If you take a look at QFileDialog documentation, you will see that getSaveFileName() is an static function. Because of this, there is no way for this method to access a member of the instance of the class that makes use of setDefaultSuffix(). So whatever you set in fileDialog->setDefaultSuffix(...) has nothing to do with what the getSaveFileName() function does.
In ordertTo make it work, you have to run the dialog directly from the instance. You should do something like this:
QFileDialog fileDialog(this, "Choose file to save");
fileDialog.setDefaultSuffix("json");
fileDialog.setNameFilter("json-files (*.json)");
fileDialog.exec();
QFile f(fileDialog.selectedFiles().first());
QFileInfo fileInfo(f);
QString FILE_NAME(fileInfo.fileName());

When is QCoreApplication valid?

I want to get the application path however when I run the application in Qt Creator, the applicationPath() method returns an empty string:
int main(int argc, char *argv[]) {
QLoggingCategory::setFilterRules("*.info=false\n");
QCoreApplication::setOrganizationName("company name");
QCoreApplication::setOrganizationDomain("companydomain.com");
QCoreApplication::setApplicationName("AppName");
#ifdef QT_DEBUG
//Install logging message handler
Logger::LogManager::setApplicationPath(QCoreApplication::applicationFilePath());
qInstallMessageHandler(Logger::LogManager::logMsg);
qDebug() << "built with debug";
#else
qDebug() << "built for release";
#endif
...
I've resolved this issue name by changing my routine to get the application name as that's really what I needed:
Logger::LogManager::setApplicationName(QCoreApplication::applicationName());
Anyway, QCoreApplication::applicationFilePath() and QCoreApplication::applicationDirPath() are available after creation of a QCoreApplication instance. If you need to get the application path before creation of a QCoreApplication instance, you will need to refer to argv[0] and parse it for your use-case.
I'll explain the issue even when your actual problem was different.
QCoreApplication computes the file path and the default application name based on the executable. The executable is taken from the first command-line argument, argv[0].
You must first instantiate the QCoreApplication with those arguments (even when they are static, they access an internal singleton which must be initialized).
Actually, Qt gives you a console warning when accessing such methods without any previous instance:
QCoreApplication::applicationFilePath: Please instantiate the QApplication object first
#include <QCoreApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
qDebug() << "applicationFilePath" << QCoreApplication::applicationFilePath();
qDebug() << "applicationName" << QCoreApplication::applicationName();
QCoreApplication a(argc, argv);
qDebug() << "applicationFilePath" << QCoreApplication::applicationFilePath();
qDebug() << "applicationName" << QCoreApplication::applicationName();
return a.exec();
}
Output
CoreApplication::applicationFilePath: Please instantiate the QApplication object first
applicationFilePath ""
applicationName ""
applicationFilePath "D:/src/stackoverflow/59355035/debug/59355035.exe"
applicationName "59355035"

How to set OffTheRecord profile for QWebEngineView?

How to setup OffTheRecord profile for QWebEngineView?
I use QT5.10 for Linux.
I am going to use it in embedded environment with read-only filesystem and I need to prevent WebEngine writing files and creating folders in filesystem.
#include <QApplication>
#include <QWebEngineView>
#include <QWebEngineSettings>
#include <QWebEngineProfile>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QWebEngineView view;
auto profile = view.page()->profile();
profile->setHttpCacheType(QWebEngineProfile::MemoryHttpCache);
profile->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies);
//profile->setPersistentStoragePath(nullptr);
std::cout << "StoragePath: " << profile->persistentStoragePath().toStdString() << std::endl;
std::cout << "isOffTheRecord: " << profile->isOffTheRecord() << std::endl;
profile->settings()->setAttribute(QWebEngineSettings::AllowRunningInsecureContent, true); // Since Qt5.7
profile->settings()->setAttribute(QWebEngineSettings::XSSAuditingEnabled, false);
view.setUrl(QUrl(QStringLiteral("http://localhost/index.html")));
view.resize(1920, 1080);
view.show();
return a.exec();
}
Try this configuration:
First of all, disable any possible cookie. Use setPersistentCookiesPolicy and set it to NoPersistentCookies
If you can write in to a given folder, try to save all temporal files in a secure storage:
auto *profile = QWebEngineProfile::defaultProfile();
profile->setCachePath("yourfolder");
profile->setPersistentStoragePath("yourfolder");
This should give you the control of all the temporal files that are generated by the Web Engine.
If not, taking a look in to Qt repo, you can see that the variable that manage this state is controlled in BrowserContextAdapter, this variable is set up to false, if the storage path is empty while creating the browser context.
So if you create your own QWebEngineProfile with an empty QString as path and use it as default profile:
QWebEngineProfile* profile = new QWebEngineProfile(QString(), parent)
std::cout << "isOffTheRecord: " << profile->isOffTheRecord() << std::endl; // Should return true
This can be done easily if you use it to create any single QWebEnginePage manually using this profile and set it in your QWebEngineView using setPage:
engineview->setPage(new QWebEnginePage(profile, parent));
The documentation for QWebEngineProfile's default constructor states:
Constructs a new off-the-record profile with the parent parent.
An off-the-record profile leaves no record on the local machine, and
has no persistent data or cache. Thus, the HTTP cache can only be in
memory and the cookies can only be non-persistent. Trying to change
these settings will have no effect.
Once you've created a default QWebEngineProfile, pass it to a QWebEnginePage and set that as the page in your QWebEngineView.
Here's a simple example that compiles and runs (tested on Mac OS):
#include <QApplication>
#include <QWebEngineView>
#include <QWebEngineSettings>
#include <QWebEnginePage>
#include <QWebEngineProfile>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWebEngineView view;
QWebEngineProfile profile;
QWebEnginePage page(&profile);
qDebug() << "StoragePath:" << profile.persistentStoragePath();
qDebug() << "isOffTheRecord:" << profile.isOffTheRecord();
view.setPage(&page);
view.setUrl(QUrl(QStringLiteral("http://www.stackoverflow.com/")));
view.show();
return a.exec();
}
When running the above you should see this appear in standard out:
StoragePath: ""
isOffTheRecord: true

QT 5.4 QPrinterInfo::printerName returns blanks

I am trying to instantiate a printer without using QPrintDialog as our GUI is QML where QPrintDialog does not exist (we are creating a printer selection dialog in QML). I am calling two invokable C++ functions 1) one function retrieves a list of valid printers and passes that back to QML, and 2) second function instantiate a printer name that was selected by the user in QML and then prints to a painter. I am using Ubuntu Linux (32) with Qt5.4.0. One interesting issue I uncovered is that when I use QPrinterInfo::availablePrinterNames() a valid list of printer names is found. When I get a list of QPrinterInfo object via using static function QPrinterInfo::availablePrinters(), then traverse the list and display the names in printerName, an empty string is returned. The documentation says that this should be a unique ID for the printer, not an empty string?????
Here's is a extract which demonstrates the issue:
#include <QCoreApplication>
#include <QString>
#include <QStringList>
#include <QtPrintSupport/QPrinter>
#include <QtPrintSupport/QPrintDialog>
#include <QtPrintSupport/QPrinterInfo>
#include <QDebug>
#include <QList>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "Printer names via availablePrinterNames method";
QStringList name_list = QPrinterInfo::availablePrinterNames();
foreach ( QString name, name_list)
qDebug() << name;
qDebug() << "\nPrinter names via availablePrinters method";
QList<QPrinterInfo> info_list = QPrinterInfo::availablePrinters();
foreach ( QPrinterInfo info, info_list )
qDebug() << info.printerName();
return a.exec();
}
I have not tried this with earlier versions of QT or the Windows version. Does anyone have any hints?
Thanks
In short: The reason for the issue is that CUPS supports driverless printers (info), and Qt does not.
Qt uses CUPS API to return the list of printers in system (availablePrinterNames) without checking, but when it constructs QPrinterInfo, it checks if the printer has a PPD driver. If not, the printer is considered invalid, and Qt returns empty name for it.
Please try this, it worked for me. First you should add windowsprintersupport.dll to your project.
int count = 0;
QList<QPrinterInfo> info_list = QPrinterInfo::availablePrinters();
foreach ( QPrinterInfo info, info_list )
{
count++;
qDebug()<< "Printer_"<< count<< ": " << info.printerName() << "State: " << info.state();
if(info.printerName() == "YOUR_PRINTER_NAME")
{
if (info.state() == 0)
qDebug()<< "Printer Idle";
else if (info.state() == 1)
qDebug()<< "Printer Active";
else if (info.state() == 2)
qDebug()<< "Printer Aborted";
else if (info.state() == 3)
qDebug()<< "Printer Error";
else
qDebug()<< "Printer Undefined Error";
}
}