I am using QProcess::start to launch Qt Assistant with my custom help project file. It works fine until i load project(not help project file) to my programm. Programm generates images from specific data using custom library. Even when all processes ends and i see generated images and nothing else happens, when i trying to launch Qt Assistant, my programm hangs at QProcess:start function when trying to start process. The code is:
show() function(public):
if (!run())
return false;
QByteArray ba("setSource ");
ba.append("qthelp://insyn_help/doc/");
proc->write(ba + page.toLocal8Bit() + '\n');
return true;
run() function(private):
if (!proc)
proc = new QProcess();
if (proc->state() == QProcess::Running)
return true;
QString app = QString(QT_BIN_DIR) + QDir::separator() + QString("assistant");
QString path = QString(PREFIX) + QString(HELP_INSTALL_PATH) + QString("/help_project.qhc");
QStringList args;
args << QLatin1String("-collectionFile")
<< QLatin1String(path.toLatin1())
<< QLatin1String("-enableRemoteControl");
QFileInfo help_project(path);
if (help_project.exists()) {
proc->start(app,args);
if (!proc->waitForStarted()) {
m_exitCode = 1;
emit closed();
return false;
}
}
This code is a part of AssistantLauncher class which was registered using qmlRegisterType and added to main.qml as a member of application window. My programm doesn't touch it anywhere (except calling a method show()). It is separate object (except it is a part of appWindow). The question is why does the process can not start only after my programm did some work? And why QProcess::start even dont have timeout.
UPD: I moved proc->start(app,args); to the child process, which i getting by using fork() and now my programm hangs on pid_t child = fork(). So the problem is that new process can not be created.
The answer is to do not use fork() because it is dangerous in big projects. More at http://www.evanjones.ca/fork-is-dangerous.html . posix_spawn also hangs my project. Now i decided to fork() new process at the beginning and send commands to it through the pipe.
Related
I'm trying to monitor changes in a folder using QFileSystemWatcher, and whenever changes happen they will be reflected in the GUI.
The idea is that I have a folder that gets updated every second with new images, and I need to display those images in sequence
nevertheless, the changes happen very fast, and the GUI freezes.
how can I make this line run in a different thread whenever there's a change in the folder.
QObject::connect(&watcher, SIGNAL(directoryChanged(QString)), w, SLOT(showModified(QString)));
or how can I make the code that creates reflections on the GUI run in a separate thread?
To make QFileSystemWatcher work in a separate thread you can do:
Create new thread - auto thread = new QThread;
Create new file system watcher - auto watcher = new QFileSystemWatcher;
Move watcher to thread - watcher->moveToThread(thread);
Make connections
connect(thread, &QThread::finished, watcher, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
connect(watcher, &QFileSystemWatcher::directoryChanged, w, &<YourWidgetClassName>::showMidified, Qt::QueuedConnection);
Start thread thread->start();
Setup watcher with addPath or addPaths
Do not forget call thread->quit(); when it's not needed or before application exits
So about problem of bunch images rendering in GUI.
I suggest you to use QImage to load and scale images for QPixmap to be created and rendered in GUI. Here QtConcurrent::run can be used per image to preform an image loading in a separate thread. It returns a QFuture object, that can be tracked with a QFutureWatcher object, that emits a finished signal, when a watched future is complete. Once connected to a QFutureWatcher::finished signal you can handle QFuture's result to assign a loaded QImage to QPixmap.
So, you need a code like this for QFileSystemWatcher::directoryChanged signal handler:
QStringList filenames = <some code to get new image filenames to render>;
auto loadImage = [](const QString &aFilename) -> QPixmap {
auto image = QImage(aFilename).scaled(200, 200, Qt::KeepAspectRatio, Qt::SmoothTransformation);
return QPixmap::fromImage(image);
};
for (auto &filename : filenames) {
auto pixmap = QPixmap(200, 200); //create empty pixmap of desired size
auto label = new QLabel;
label->setPixmap(pixmap);
m_layout->addWidget(label);
auto future = QtConcurrent::run(loadImage, filename);
auto watcher = new QFutureWatcher<QPixmap>(this);
connect(watcher, &QFutureWatcher<QPixmap>::finished, this, [watcher, label]() { label->setPixmap(watcher->result()); });
connect(watcher, &QFutureWatcher<QPixmap>::finished, watcher, &QFutureWatcher<QPixmap>::deleteLater);
watcher->setFuture(future);
}
I'm considering to use QProcess to call a command line app (gpio) multiple times. Every time user clicks a button then a command is issued.
And the app output will be monitored and redirected to screeen. The code looks like the following.
void Gpio::command(QString argument)
{
// if(process)
// delete process;
process = new QProcess(this);
connect(process, SIGNAL(started()), this, SLOT(onStart()));
connect(process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(onFinish(int,QProcess::ExitStatus)));
connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(readGpio()));
QString program("gpio");
QStringList list = argument.split(" ");
process->start(program, list);
}
Question: Should I delete process? Doing so I got:
QProcess: Destroyed while process is still running.
Monitoring exitCode and exitStatus I see they are always 0.
This question concerns more about the proper use of QProcess while "QProcess and shell : Destroyed while process is still running" focus on the specific error.
as you don't want to run multiple processes concurrently (as per the comments), you don't need to create / delete the QProcess multiple times.
gpio.h
QProcess* m_gpioProcess;
gpio.cpp file
Gpio::Gpio(.....),
.....(),
m_gpioProcess(new QProcess(this))
{
m_gpioProcess->setProgram("gpio");
connect(m_gpioProcess, SIGNAL(started()), this, SLOT(onStart()));
connect(m_gpioProcess, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(onFinish(int,QProcess::ExitStatus)));
connect(m_gpioProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(readGpio()));
}
void Gpio::command(const QString& args)
{
if (m_gpioProcess->state() != QProcess::NotRunning) {
qDebug() << "Process already running, ignoring the request";
return;
}
m_gpioProcess->setArguments(args.split(" "));
m_gpioProcess->start();
if (m_gpioProcess->waitForStarted()) {
qDebug() << "Process started with arguments:" << m_gpioProcess->arguments();
}
}
if you want to prevent user clicking the button multiple times, consider enabling / disabling the button as per m_gpioProcess state.
for Qt 4.8, just remove this line
m_gpioProcess->setProgram("gpio");
and this line
m_gpioProcess->setArguments(args.split(" "));
and change this line
m_gpioProcess->start();
to
m_gpioProcess->start("gpio", args.split(" "));
Well, It seems I was forgetting process->waitForFinished(); after the call to start. This seems to solve the problem.
add following code maybe helpful:
process->close();
I am using the following code in order to generate QPixmap* pointers and then insert them into QHash<QString, QPixmap*> (I will show only the pointers generation code since this is the one that fails).
QPixmap* MyClass::loadImg(QString fileName)
{
QImage qimage(fileName);
if (qimage.isNull()) {
qDebug() << "Cannot load image " << fileName;
}
QPixmap *image = new QPixmap(fileName);
return image;
}
The problem that I have is the following:
For the first about 200 calls the method works fine - it is being called on a loop that iterates through the image files of a directory. Then suddenly the QPixmap* starts returning QPixmap(null) for no apparent reason.QImage is also null when that happens.
I have checked and made sure that the path is fine. Also, I have tried with various sets of images and the same always happens - it runs with no problems the ~200 calls and then starts generating nulls.
Any help would be appreciated.
Thank you.
Just don't create it on heap. QPixmap is implicitly shared.
I'm getting the following error when trying to use the Direct Show 9 backend with qt's phonon framework:
Pins cannot connect due to not supporting the same transport. (0x80040266)
Does anyone know what this error means and/or how to fix it? Is this a problem with the Direct Show 9 backend for phonon?
Apparently the problem has to do with bad metadata. If the Id3 tags aren't just right, the direct show 9 backend chokes on them. I solved the problem by writing the following function:
void removeTags(UDJ::DataStore::song_info_t& song){
static int fileCount =0;
if(song.source.fileName().endsWith(".mp3")){
UDJ::Logger::instance()->log("On windows and got mp3, copying and striping metadata tags");
QString tempCopy = QDesktopServices::storageLocation(QDesktopServices::TempLocation) + "/striped" + QString::number(fileCount) +".mp3";
if(QFile::exists(tempCopy)){
UDJ::Logger::instance()->log("Prevoius file existed, deleting now");
if(QFile::remove(tempCopy)){
UDJ::Logger::instance()->log("File removal worked");
}
}
bool fileCopyWorked = QFile::copy(song.source.fileName(), tempCopy);
if(!fileCopyWorked){
UDJ::Logger::instance()->log("File copy didn't work");
return;
}
TagLib::MPEG::File file(tempCopy.toStdString().c_str());
file.strip();
file.save();
Phonon::MediaSource newSource(tempCopy);
song.source = newSource;
if(fileCount == 3){
fileCount =0;
}
else{
fileCount++;
}
}
}
song_info_t is just a struct with a Phonon::MediaSource member in it called source. The function works by using taglib to strip off all of the metadata for a song and save the new song as a temporary file. The function also rotates the filename is uses for the temporary file so that it doesn't create an infinite number of temporary copy files. I hope this helps anyone else who is having this error.
i am wondering if its possible to solve this problem.
Ive got qt application and if user tick the checkbox, i want this application to launch on startup of operating system.
Ive already googled, and ive come up with this solution>
my QT application needs admin privileges in order to modify registry, so
create manifest file ( <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>)
2.apply this command
mt -manifest manifestfile -outputresource:binfile.exe;1
3.use this piece of code in QT to modify registry
void MainWindow::set_on_startup() {
QSettings settings("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
if (ui->checkBox->checkState()) {
QString value = QCoreApplication::applicationFilePath(); //get absolute path of running exe
QString apostroph = "\"";
#ifdef DEBUG
ui->textEdit->append(QCoreApplication::applicationFilePath ());
#endif
value.replace("/","\\");
value = apostroph + value + apostroph + " --argument";
#ifdef DEBUG
ui->textEdit->append(value);
#endif
//write value to the register
settings.setValue("name", value);
}
else {
settings.remove("name");
}
}
So, this looks good right ?
BUT... application with default admin priveleges cant be launched on startup of operating system, BUT application without admin priveleges cant modify registry. So , there is one solution - tell a user, that if he wants to set this "startup" option, he first needs to start application as admin, then the application will be able to modify registry, and default privileges will remain "asInvoker", but this seems really impractical and i think that users will be discouraged by this.
So, how to solve this problem ? how other applications solve this problem ?
You won't need admiministrator privileges if you use following key:
QSettings settings("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
Notice
HKEY_CURRENT_USER
instead of using
HKEY_LOCAL_MACHINE
My 2 cents! : )
Why not simply put the app shortcut in "Startup" folder.
Qt provides a cross-platform way of determining the paths to many default system directories using the QDesktopServices class.
(Source: Thanks to Dave Mateer for his answer to this question.)
The method is:
QDesktopServices::storageLocation(QDesktopServices::ApplicationsLocation)
This gives (on my Win 7):
C:\Users\user_name\AppData\Roaming\Microsoft\Windows\Start Menu\Programs
and all we need is:
C:\Users\user_name\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
Simple!
I use this without any hassle of UAC or any kind of rights problem in most of my apps.
This may not be the best way... but it is certainly an easy way.
(Please pitch in thoughts/comments if this approach has any big disadvantages.)
Update:
To create the short-cut for the application in startup folder, use this code:
QFileInfo fileInfo(QCoreApplication::applicationFilePath());
QFile::link(QCoreApplication::applicationFilePath(), QDesktopServices::storageLocation(QDesktopServices::ApplicationsLocation) + QDir::separator() + "Startup" + QDir::separator() + fileInfo.completeBaseName() + ".lnk");
I hope this helps! : )
Include this header QSettings
#include <QSettings>
And add this into your code.
QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run",
QSettings::NativeFormat);
settings.setValue("YourApplicationName",
QCoreApplication::applicationFilePath().replace('/', '\\'));
For everybody who are trying to solve the problem, this is the 100% working solution:
How can I ask the user for elevated permissions at runtime?
create app1.exe
create app2.exe with admin priveleges - tutorial is in the first post (manifest file, mt.exe, etc..)
when user tick the checkbox in app1.exe, i call the the app2.exe (for example with no arguments) - you can find all function for this # the link ive just posted above
// well, in fact, you dont have to use the function from the example above:
i find this call much better
QObject *parent = new QObject();
QString program = AppToExec; //"/path/to/the/app2.exe"
QStringList arguments ;
arguments << ""; //just in case we want arguments
QProcess *myProcess = new QProcess(parent);
myProcess->start(program);
app2.exe, for example
QApplication a(argc, argv);
MainWindow w;
// w.show();
if (argc == 1) {
w.test();
a.quit();
}
problem solved.
Using this link
create stratup app and code:
void make_startup_app(){
QString appName = "app.exe";
QString appNameLink = appName+".lnk";
QFile::link(appName, appNameLink);
QString userName = QDir::home().dirName();
QString dir_startup = "C:/Users/" + userName +
"/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup/"+ appNameLink;
QFile::copy(appNameLink, dir_startup);
}