Error from KGlobal::locale while opening a QFileDialog in C++ - c++

I've got this C++ code:
bool load ()
{
const QString * filename = openFileDialog(QFileDialog::AcceptOpen, QFileDialog::ExistingFile);
if (filename != 0)
return load(*filename);
else return false;
}
const QString * openFileDialog (QFileDialog::AcceptMode acceptMode, QFileDialog::FileMode mode)
{
QFileDialog dialog;
dialog.setAcceptMode(acceptMode);
dialog.setFileMode(mode);
dialog.show();
QStringList files = dialog.selectedFiles();
if (files.isEmpty()) return 0;
return new QString(files.at(1));
}
Now, I get the following error:
KGlobal::locale::Warning your global KLocale is being recreated with a valid main component instead of a fake component, this usually means you tried to call i18n related functions before your main component was created. You should not do that since it most likely will not work
The file dialog never appears.
I'm using Debian wheezy (german), KDE 4.8.4, Qt 4.8.2 and GCC 4.7.2

I'm late to the party, but this is apparently a very old issue anyway. It was solved 5 years ago, here: http://pyqt.riverbankcomputing.narkive.com/GMKOTskG/a-dire-warning-message#post16
The short answer:
When opening the file dialog, it's using the "system" file dialog. In this case, the system dialog is KDE, and an issue that is blamed on a KDE library produces that warning. Want to avoid the issue? Don't use the "system" file dialog.
In your openFileDialog function, simply add:
dialog.setOption(QFileDialog::DontUseNativeDialog)
This solution worked for me. (I'm using PyQt4, but was having the same symptoms.)

Related

Qt startDetached fails under Qt 5.15.2

I'm updating some old Qt 5.6 code to Qt 5.15.2
The Qt C++ code below opens a dos prompt, the runs a bat file, staying open when done.
cstring = "cmd /k " +QDir::currentPath()+"/cpath.bat";
QProcess::startDetached(cstring);
This code works fine under 5.6, does nothing under 5.15.2
How do I fix this for Qt 5.15.2?
You need to split the command to program and arguments. See https://doc.qt.io/qt-5/qprocess.html#startDetached-1
In your case it should be
QProcess::startDetached("cmd", {"/k", QDir::currentPath() + "/cpath.bat"});
The advantage is that now you do not need to worry for example about enclosing paths containing spaces in quotes. Consider if your current path would be "C:\my path". Then your version from Qt 5.6 would not work. While the version I presented in this answer will work out of the box. I guess it was exactly this reason that the old overload was prone to this kind of bugs that Qt decided to remove it and use only the new overload.
Ok, with my previous answer I though that the problem was with the way you are passing the arguments. But as it seems the problem is with showing the terminal... Since I am doing the same in one of my projects (HiFile file manager), I had a look how I am doing it. So here it is.
class DetachableProcess : public QProcess
{
public:
explicit DetachableProcess(QObject *parent = nullptr) : QProcess(parent)
{
}
bool detach()
{
if (!waitForStarted())
{
return false;
}
setProcessState(QProcess::NotRunning);
return true;
}
};
And then use it like this:
DetachableProcess process;
process.setCreateProcessArgumentsModifier( // this probably did the trick you need
[](QProcess::CreateProcessArguments *args)
{
args->flags |= CREATE_NEW_CONSOLE;
args->startupInfo->dwFlags &=~ STARTF_USESTDHANDLES;
});
process.start("cmd", {"/k", QDir::currentPath() + "/cpath.bat"}); // here are your params...
process.detach();
I am afraid I cannot give more explanation why this complicated method works, I do not know it. And I do not know why it was changed in Qt.
I hope it will work for you. It works for me in Qt 6.2.4, which I am currently using.

How to open a file with Qt when there is no default program for it?

There is a QDesktopServices::openUrl function in Qt which opens files with default programs, like when you want to open .docx file with Microsoft Office. However, the function will simply return 0 and do nothing if there is no default program assotiated with the file extension of the requested file. I would like Qt to show something like this instead:
A cross-platform solution would be ideal.
Is it possible with Qt?
This one works for me. But I didn't test it anywhere except my Windows 7 machine
QDesktopServices::openUrl(QUrl::fromLocalFile("D:/file"));
Try something like this. It should open the dialog you need. But on WIndows 10 it does not show the checkbox, I am not sure why.
#include <ShlObj.h>
bool openWith(const QString &filePath)
{
QString nativePath = QDir::toNativeSeparators(filePath);
OPENASINFO oi = {};
oi.pcszFile = reinterpret_cast<LPCWSTR>(nativePath.utf16());
oi.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC;
return SHOpenWithDialog(NULL, &oi) == S_OK;
}

QFileDialog dynamic translation

This question has unfortunately been asked before but I'm going insane here.
In my Qt Application the user is able to dynamically change the language which works perfect for all my own translations. It does not work for my calls to QFileDialog. The respective code:
void myapp::change_language(std::string& lang_str){
// my own translations works
qApp->removeTranslator(&this->app_lang);
this->app_lang.load(QString::fromStdString(lang_str));
qApp->installTranslator(&this->app_lang);
// system translations, works not for qfiledialog
qApp->removeTranslator(&this->app_lang_qt);
bool test = this->app_lang_qt.load("qt_fr.qm"); // returns true
qApp->installTranslator(&this->app_lang_qt);
}
And
void myapp::changeEvent(QEvent* event){
if(event->type() == QEvent::LanguageChange){
this->ui.retranslateUi(this);
}
QMainWindow::changeEvent(event);
}
With
QTranslator app_lang;
QTranslator app_lang_qt;
The fixed string "qt_fr.qm" is just for testing purpose because french is easily detectable.
What I want is to change the language in static calls to QFileDialog and QMessageBox but the language is only changed in QMessageBox, not in QFileDialog. For both classes I'm only calling the static members to that can't be the issue. I also tried to install this translator in the main.cpp with the same results.
By default, QFileDialog will use the native file browser rather than a custom Qt-based dialog. The native file browser is will be using the OS language, rather than the Qt language, and will not have Qt translations applied to it. You can override this behaviour using the DontUseNativeDialog option for QFileDialog.

Will loading a DLL dynamically reconcile its stderr to a main application? If so, then how...?

I'm writing a GUI application, using Qt, which links to a third-party DLL that sometimes sends error messages to stderr. I'd like these error messages to be displayed in a window within my GUI.
I couldn't find an established way to redirect stderr (as opposed to std::cerr) even after much searching, so I wrote the following class myself:
class StdErrRedirect : public QObject
{
Q_OBJECT
public:
// Constructor
StdErrRedirect(QTextEdit *errorLog,
QObject *parent = NULL);
// Destructor
~StdErrRedirect();
private slots:
void fileChanged(const QString &filename);
private:
QFile tmp;
QFileSystemWatcher watcher;
QString tmpFileNameQtFormat;
QString tmpFileNameNativeFormat;
QTextEdit *m_errorLog;
QString oldContent;
};
StdErrRedirect::StdErrRedirect(QTextEdit *errorLog,
QObject *parent)
: QObject(parent)
{
// Store the pointer to the error log window
m_errorLog = errorLog;
// Create a temporary filename: first find the path:
tmpFileNameQtFormat = QDir::tempPath();
// Make sure the closing slash is present:
if (!tmpFileNameQtFormat.endsWith(QChar('/')))
tmpFileNameQtFormat.append(QChar('/'));
// Add the file name itself:
tmpFileNameQtFormat.append("nb_stderrlog");
// Obtain a version of the filename in the operating system's native format:
tmpFileNameNativeFormat = QDir::toNativeSeparators(tmpFileNameQtFormat);
// Set up redirection to this file:
freopen(tmpFileNameNativeFormat.toAscii().constData(), "a+", stderr);
// Initialise the QFileSystemWatcher:
connect(&watcher, SIGNAL(fileChanged(const QString &)),
this, SLOT(fileChanged(const QString &)));
watcher.addPath(tmpFileNameQtFormat);
tmp.setFileName(tmpFileNameQtFormat);
}
StdErrRedirect::~StdErrRedirect()
{
// Ensure the temporary file is properly deleted:
fclose(stderr);
tmp.close();
tmp.open(QIODevice::ReadWrite);
tmp.remove();
}
void StdErrRedirect::fileChanged(const QString &filename)
{
tmp.open(QIODevice::ReadOnly);
QTextStream stream(&tmp);
QString content = stream.readAll();
tmp.close();
// Identify what's new, and just send this to the window:
int newchars = content.size() - oldContent.size();
if (newchars)
{
m_errorLog -> append(content.right(newchars));
oldContent = content;
}
}
If I instantiate this from my main window using:
errorLog = new QTextEdit;
redirector = new StdErrRedirect(errorLog);
... then everything I write to stderr appears in the window.
So far, so good. The problem is that the DLL's output still does not. In a call to a DLL function which emits an error, if I put the code:
if (error != _OK)
{
error.PrintErrorTrace();
fprintf(stderr, "Should have printed an error \r\n");
fflush(stderr);
//fsync(_fileno(stderr)); Linux version
_commit(_fileno(stderr));
return;
}
...then the text "Should have printed an error" appears but the error message itself does not.
Now, I've read somewhere that this is probably because the redirection is being set up after the DLL was loaded at the beginning of the application, and so it's own stderr channel is unaffected. Therefore, I should be able to fix this by loading the DLL dynamically, after setting up the redirection, instead.
Here is my question, then: how do I do this? I can try putting the following code at the beginning of my application:
QLibrary extlib;
extlib.setFileName("libname");
extlib.setLoadHints(QLibrary::ResolveAllSymbolsHint);
extlib.load();
...but on its own it has no effect. I think this is because the linker is still setting the library up to be opened automatically. However, if I remove the DLL from the linker (I'm using VS2008, so I remove extlib.lib from the dependency list) then the application won't compile because the compiler can't find the symbols from the DLL.
So there's obviously something deeply wrong with what I'm trying to do here. Can anybody help?
Thanks,
Stephen.
Does the DLL really write to stderr? Or does it write to GetStdHandle(STD_ERROR_HANDLE) ? The first maps to the second, initially. But with freopen() you merely change the mapping. Anything written to STD_ERROR_HANDLE will still go there.
To redirect everyones error output, you would need SetStdHandle.
There is only one stderr, so my guess is that the problem is not that you need to load the dll dynamically, but somewhere in your redirection code. Your redirection code is written Linux style, where in windows things work differently.
if you could test your application on Linux, It would help to pin point the problem. If it works on Linux, that it is surly the redirection code.
Anyway, you should read some more about redirection and windows, as I don't think that what you are trying to do now will help you.

Programmatically selecting file in explorer

In my application I can programmatically open explorer and select a file using the following code:
void BrowseToFile(LPCTSTR filename)
{
CString strArgs;
strArgs = _T("/select,\"");
strArgs += filename;
strArgs += _T("\"");
ShellExecute(0, _T("open"), _T("explorer.exe"), strArgs, 0, SW_NORMAL);
}
My problem is that if I call this function a second time with a different file, but in the same folder, the selection in explorer does not change to the new file, but remains on the previous file.
For example, if I call my function with C:\path\to\file1.txt, a new explorer window will open and file1.txt will be selected. If I call my function a second time with C:\path\to\file2.txt, the existing explorer window will be activated, but the selection will still be on file1.txt.
Is there a way to force explorer to update the selection or a better way to accomplish this?
EDIT:
The behavior mentioned above was on Windows XP. It seems the behavior on Vista / Win7 is different. Each call will open a new instance of explorer and select the file.
My main goal is to replicate the Visual Studio option to Open Containing Folder of a document. This feature in Visual Studio behaves the same on XP, Vista, and Win7. It will not create a new instance if another instance with the same folder is already open, but it will update the selection to the new file.
If anybody knows how Visual Studio accomplishes this I would love to know about it.
Found the answer to my question. I need to use the shell function SHOpenFolderAndSelectItems. Here is the code for the function if anybody is ever interested:
void BrowseToFile(LPCTSTR filename)
{
ITEMIDLIST *pidl = ILCreateFromPath(filename);
if(pidl) {
SHOpenFolderAndSelectItems(pidl,0,0,0);
ILFree(pidl);
}
}
Try the '/n' option. This will, however, open a new folder - perhaps already opened. But, at least, the file you specify is selected.
/n,/select,<path_and_filename>
SHOpenFolderAndSelectItems always fails in my case and I can't figure out why. Btw, you must call CoInitialize/CoInitializeEx before calling this one.
In the case you outlined it appears the file window only selects the file when it's initialized instead of when activated.
Although this feels like a kludge, you could detect XP and only for that OS close the dialog using its handle and open a new one to target another file with.