QT add Item trigger redraw, not freezing - qt

i'm using QT for the first time and got some problems with refreshing the GUI while adding elements.
The Code looks like:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
PObj obj;
MainWindow mw;
qRegisterMetaType<std::string>();
QObject::connect(&obj, SIGNAL(setText(std::string const&)),
&mw, SLOT(appendText(std::string const&)));
QFuture<void> f1 = QtConcurrent::run(&obj, &PObj::process);
mw.show();
f1.waitForFinished();
return a.exec();
}
With the PObj::process definition:
void PObj::process()
{
for(; ;)
{
sleep(1);
//do work and set text
std::string text = "bla";
emit setText( text );
}
}
And the MainWindow::appendText slot:
void MainWindow::appendText(std::string const& str )
{
ui->listWidget->addItem(QString::fromStdString(str));
}
I've tried placing qApp->processEvents() ,QCoreApplication::processEvents(); ... running wit future in the ThreadPool.
I thought running them with Concurrent::run is enough ?
UPDATE:
The question is, why the GUI isnt refreshed every second a new item is added ?

The f1.waitForFinished(); calls blocks until f1 is finished, as the name implies. This will never happen because you have the infinite loop. So your code will never get to main loop. You can't block the main thread like that! In general, avoid any WaitForXxxx() methods, especially the GUI thread.
Also, you have no way of stopping the process(); anyway, so waiting for it to finish doesn't make any sense... You might want to add a way to tell it to stop (such as atomic variable) but anyway, to fix your problem, simply remove the f1.waitForFinished(); line.
To terminate the task nicely, try adding QAtomicInt flag (not volatile boolean, it won't do), and then change the code like this:
Add member variable to PObj (should make it private and add setter):
QAtomicInt termianteFlag;
Change main like this:
int main(int argc, char *argv[])
{
///snip
QFuture<void> f1 = QtConcurrent::run(&obj, &PObj::process);
mw.show();
int ret = a.exec();
f1.terminateFlag = 1; // change this to setter method
f1.waitForFinished(); // this is not ideal, will wait for up to a second before exit
}
and
void PObj::process()
{
while(!terminateFlag)
{
sleep(1);
//do work and set text
std::string text = "bla";
emit setText( text );
}
}

Related

Why can't I catch the OOM exception?

I have a function that reads a large file to fill a QStringList. The program crashes probably because there is not enough memory because if I use a small file the program runs well. I try to debug the problem by catching the exception.
QStringList readlargefile(QString filename)
{
QStringList result;
QFile file(filename);
if (!file.open(QIODevice::ReadOnly))
{
qDebug()<<"cannot open file: "<<filename;
return result;
}
QTextStream in(&file);
in.setCodec("UTF-8");
QString line;
while(in.readLineInto(&line))
{
if(!line.isEmpty())
result<<line;
}
file.close();
return result;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QStringList result;
try
{
qDebug()<<"reading file";
result=readlargefile("largefile.txt");
}
catch(...) {
qFatal("got exception");
}
}
The output is:
reading file
Killed
I cannot catch the exception, why?
If your program is aborted by OS it will not generate any exception. But you can setup a signal handler:
void signalHandler(int)
{
//...
}
int main(int argc, char* argv[])
{
signal(SIGINT , signalHandler);
signal(SIGTERM , signalHandler);
#ifdef Q_OS_WIN
signal(SIGBREAK, signalHandler);
#endif
The reason you can't catch std::bad_alloc is because Qt probably uses a no-throw version of ::new. Or new is OK but the Princess is in another castle.
There are two pitfalls you may stumble upon with your original problem (crash).
1. It can be a reallocating issue.
When the array is filled up already, and you try to insert more, it allocates a new array and copies (moves) data from the previous one. So you end up having two big arrays until copying (moving) is done. If you know the exact number of strings ahead, you can try preallocating the array to ensure there will be no reallocations. Use QList::reserve() for that.
2. Qt containers like QList and QVector can hold no more than 2GB of data.
If sizeof(QString) is 8 bytes, there will be allowed no more than 2^28 items.
It will crash eventually if you try to store more. Try std::vector (with reserve) and check if it works.
After all, if your system doesn't have enough memory for the task - it doesn't have enough memory, and there is nothing you can do about it but to change your algorithm.

How to prevent the QBasicTimer::stop: Failed warning when objects become threadless?

QObjects can easily become threadless, when their work thread finishes ahead of them. When this happens, Qt doesn't release their timer ids, even though the timers are not active anymore. Thus, a QBasicTimer::stop: Failed. Possibly trying to stop from a different thread warning appears. It has mostly cosmetic consequences, but does indicate a timer id leak, and thus a workaround would be nice to have. The following example triggers the problem:
#include <QtCore>
int main(int argc, char *argv[]) {
static_assert(QT_VERSION < QT_VERSION_CHECK(5,11,0), "");
QCoreApplication app(argc, argv);
QObject object;
object.startTimer(1000);
QThread workThread;
workThread.start();
object.moveToThread(&workThread);
QTimer::singleShot(500, &QCoreApplication::quit);
app.exec();
workThread.quit();
workThread.wait();
}
It'd be nice if the workaround didn't have to make any modifications to how the timers are allocated, i.e. that there would be no extra tracking of timers needed beyond what Qt already does.
A simple solution is to prevent the problem: if the object is about to become threadless, move it to the thread handle's parent thread, and then when the thread itself is about to be destructed, reestablish the object's timers to prevent the warning.
QObject's moveToThread implementation has two parts:
The QEvent::ThreadChange is delivered to the object from moveToThread. QObject::event uses this event to capture and deactivate the timers active on the object. Those timers are packaged in a list and posted to the object's internal _q_reactivateTimers method.
The event loop in the destination thread delivers the metacall to the object, the _q_reregisterTimers runs in the new thread and the timers get reactivated in the new thread. Note that if _q_reregisterTimers doesn't get a chance to run, it will irrevocably leak the timer list.
Thus we need to:
Capture the moment the object is about to become threadless, and move it to a different thread, so that the QMetaCallEvent to _q_reactivateTimers won't be lost.
Deliver the event in the correct thread.
And so:
// https://github.com/KubaO/stackoverflown/tree/master/questions/qbasictimer-stop-fix-50636079
#include <QtCore>
class Thread final : public QThread {
Q_OBJECT
void run() override {
connect(QAbstractEventDispatcher::instance(this),
&QAbstractEventDispatcher::aboutToBlock,
this, &Thread::aboutToBlock);
QThread::run();
}
QAtomicInt inDestructor;
public:
using QThread::QThread;
/// Take an object and prevent timer resource leaks when the object is about
/// to become threadless.
void takeObject(QObject *obj) {
// Work around to prevent
// QBasicTimer::stop: Failed. Possibly trying to stop from a different thread
static constexpr char kRegistered[] = "__ThreadRegistered";
static constexpr char kMoved[] = "__Moved";
if (!obj->property(kRegistered).isValid()) {
QObject::connect(this, &Thread::finished, obj, [this, obj]{
if (!inDestructor.load() || obj->thread() != this)
return;
// The object is about to become threadless
Q_ASSERT(obj->thread() == QThread::currentThread());
obj->setProperty(kMoved, true);
obj->moveToThread(this->thread());
}, Qt::DirectConnection);
QObject::connect(this, &QObject::destroyed, obj, [obj]{
if (!obj->thread()) {
obj->moveToThread(QThread::currentThread());
obj->setProperty(kRegistered, {});
}
else if (obj->thread() == QThread::currentThread() && obj->property(kMoved).isValid()) {
obj->setProperty(kMoved, {});
QCoreApplication::sendPostedEvents(obj, QEvent::MetaCall);
}
else if (obj->thread()->eventDispatcher())
QTimer::singleShot(0, obj, [obj]{ obj->setProperty(kRegistered, {}); });
}, Qt::DirectConnection);
obj->setProperty(kRegistered, true);
}
obj->moveToThread(this);
}
~Thread() override {
inDestructor.store(1);
requestInterruption();
quit();
wait();
}
Q_SIGNAL void aboutToBlock();
};
int main(int argc, char *argv[]) {
static_assert(QT_VERSION < QT_VERSION_CHECK(5,11,0), "");
QCoreApplication app(argc, argv);
QObject object1, object2;
object1.startTimer(10);
object2.startTimer(200);
Thread workThread1, workThread2;
QTimer::singleShot(500, &QCoreApplication::quit);
workThread1.start();
workThread2.start();
workThread1.takeObject(&object1);
workThread2.takeObject(&object2);
app.exec();
}
#include "main.moc"
This approach can be easily extended to dynamically track all children of obj as well: Qt provides sufficient events to do such tracking.
Hold the timer id to be killed from within thread - by object:
int id = object.startTimer(1000);
QThread workThread;
workThread.start();
object.moveToThread(&workThread);
QTimer::singleShot(500, &QCoreApplication::quit);
QObject::connect(&workThread, &QThread::finished, [&](){object.killTimer(id);});
...
How about moving the object back to the main thread...
class Object : public QObject
{
public:
using QObject::QObject;
virtual ~Object() {
qDebug()<<"Object"<<QThread::currentThread()<<this->thread();
if(thread() == Q_NULLPTR)
moveToThread(QThread::currentThread());
}
};
#include <QtCore>
int main(int argc, char *argv[]) {
static_assert(QT_VERSION < QT_VERSION_CHECK(5,11,0), "");
QCoreApplication app(argc, argv);
Object object;
object.startTimer(1000);
QThread workThread;
workThread.start();
object.moveToThread(&workThread);
QTimer::singleShot(500, &QCoreApplication::quit);
qDebug()<<"main"<<QThread::currentThread()<<object.thread();
app.exec();
workThread.quit();
workThread.wait();
}

QTextDocument::contentsChanged doesn't seem to be triggered on formatting changes

I would like to do certain updates when the text contents change or font or alignment change on QGraphicsTextItem.
So I connected QTextDocument::contentsChanged() to a slot that does the update.
This signal is emitted whenever the document's content changes; for
example, when text is inserted or deleted, or when formatting is
applied.
The signal gets hit when I change the text - but setting text formatting or alignment doesn't seem to affect it.
.h
class MyTextItem : public QGraphicsTextItem
{
Q_OBJECT
public:
MyTextItem();
~MyTextItem() {}
void setItemFont(QFont f);
void setItemAlign(Qt::Alignment a);
private slots:
void updateItemOnContentsChanged();
private:
void updateTextOnPropertyChanges();
};
.cpp
MyTextItem::MyTextItem()
{
setTextInteractionFlags(Qt::TextEditorInteraction);
connect(document(), SIGNAL(contentsChanged()), this, SLOT(updateItemOnContentsChanged()));
}
void MyTextItem::setItemFont(QFont f)
{
setFont(f);
}
void MyTextItem::setItemAlign(Qt::Alignment a)
{
QTextDocument *_document = document();
QTextOption _option = _document->defaultTextOption();
_option.setAlignment(a);
_document->setDefaultTextOption(_option);
setDocument(_document);
}
void MyTextItem::updateItemOnContentsChanged()
{
updateTextOnPropertyChanges();
}
void MyTextItem::updateTextOnPropertyChanges()
{
qDebug("changing something");
}
main.cpp
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGraphicsScene s;
QGraphicsView view(&s);
s.setSceneRect(-50, -50, 500, 500);
view.show();
MyTextItem* t = new MyTextItem();
t->setPlainText("Hello World !"); // Note this triggers update
s.addItem(t);
qDebug("1");
qDebug() << t->font().family();
t->setItemFont(QFont("Arial")); // or t->setFont(QFont("Arial"));
qDebug() << t->font().family();
qDebug("2");
t->setItemAlign(Qt::AlignRight);
qDebug("3");
return app.exec();
}
My debug output:
changing something
1
"MS Shell Dlg 2" // so the font is changing - yet no signal
"Arial"
2
3
(and if I type in the item on the scene I get changing something as well)
But no changing something after the setItemFont() or setItemAlign().
So changing text affects it - but changing font or alignment doesn't...
Do I understand wrong the part I set bold in the signal description ?
Why do I not see changing something after changing font or alignment...
I also wonder - does my changing the document() when I set alignment or wrap or other document properties affect the connect ? (it doesn't seem to...)
(Note I wrote the setItem* functions to be able to call the updateTextOnPropertyChanges() function - so I can make things work the way I need - but would be great if I didn't need them and just use the Qt ones, if the signal worked as I thought it would)
It seems that what falls under formatting is loosely defined.
As you said, setItemFont() or setItemAlign() do not trigger the signal, but calling _document->setDocumentMargin(4.3); within your void MyTextItem::setItemAlign(Qt::Alignment a) method does.
Also, contentsChanged() is a signal emitted by the QTextDocument class, so I'm not sure if calling methods on the QGraphicsTextItem object that change its appearance classifies as modifying the formatting of the QTextDocument.

hide qdialog and show mainwindow

I have a Qdialog in which i get some inputs to use on my mainwindow. so it must appear first than mainwindow.
the problem is that my mainwindow does not show up. here's my main.cpp
#include <QtGui/QApplication>
#include "planevolume.h"
#include "dialog.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Dialog *dialog= new Dialog;
dialog->show();
planevolume mainwindow;
bool dialogcheck = dialog->isHidden();
if (dialogcheck==1)
{
mainwindow.show();
}
else
{
}
return app.exec();
}
I have a pushbutton that when pressed hides the qdialog and if it is hidden than the mainwindow should show up, right?
here's the SLOT i used:
void Dialog::startplanevolume()
{
if (xMax==0 || yMax==0 || zMax==0 || xMMax==0 || yMMax==0 || zMMax==0)
{
ui->label_17->setText("Error: Can't start, invalid measures");
}
else
{
hide();
}
}
the mainwindow can only start after that button is clicked as only then I have the inputs to the main winodw
So the problem here is that calling dialog->show() does not block execution. The minute that call is made, it moves on to the next method. You need to block execution until the user finishes putting input in.
Make your main like this:
QApplication app(argc, argv);
Dialog *dialog= new Dialog;
if ( dialog->exec() ) {
planevolume mainwindow;
mainwindow.show();
return app.exec();
}
return 0;
And in your dialog class, make your method look like:
void Dialog::startplanevolume()
{
if (xMax==0 || yMax==0 || zMax==0 || xMMax==0 || yMMax==0 || zMMax==0)
{
ui->label_17->setText("Error: Can't start, invalid measures");
}
else
{
this->accept(); // close the dialog with a result of 1
}
}
When you press the button, you call your Dialog::startplanevolume, yes, but that's it. You don't go back to the main loop.
If you want to display your mainwindow, you may want to call a planevolume.show() in your Dialog::startplanevolume, just after the hide.
It might be tricky if your objects are in different files, though. So maybe you could define a signal like DialogChecked, emit this signal in your Dialog::startplanevolume (after the hide, of course...), and modify your main so that it would call mainwindow.setVisible(1) when receiving a DialogChecked.
The PushButton action may happen only after app.exec() is called. It makes no sense testing dialog properties before the main loop is entered.
The expected behavior may be reached by setting up the components to start sequentially in an asynchronous way. In Qt world, this means using signals and slots.
connect(dialog, SIGNAL(accept()), &mainwindow, SLOT(show()));

Qt 4.8 killing and restarting the GUI

There is requirement of writing a Qt application on a MIPS based platform.
But there are lots of constraints. The constraints included freeing up of few resources (QGFX Plugin, GPU Memory etc) when required and re-using it. But the application cannot be killed as its handling lots of other requests and running other things.
Basically the GUI needs to be killed and free all the resources related to GUI; later when when required restart again
One of the way which has been tried is :
main() -> create a New-Thread
In the New-Thread,
while(<Condition>)
{
sem_wait(..)
m_wnd = new myMainWindow();
...
..
app->exec();
}
When ever there is a kill command, it comes out of the event loop, and wait for the signal from other threads. Once other threads does the required changes, it will get the signal and will create a new window and goes into the event loop.
In the main(), there are also few other threads created, which control other devices etc and signal the start and stop for the Qt-GUI.
The above seems to work but I am not sure if this is the right design. Does it create any problem?
Can any one suggest any better way?
I was able to find the required answer in Qt-Forums.
Since the main intention was to remove all the things related to GUI (On screen), I could use void setQuitOnLastWindowClosed ( bool quit ) (Details Here). This will make sure the GUI / Main window is closed and still the app doesnt come out of event loop and I can restart the main window later.
Thanks
When I needed a way to ensure that my app kept running, I forked it into a sub-process. That way, even if it seg-faulted, the main process would catch it and start a new child process. In the child process, I had multiple threads for GUI and non-GUI tasks. The fork code is short and is based on the example given in the wait(2) man page. The main() simply calls createChild() in a while loop. createChild() starts a new process using zmain(). zmain() is your QT app's main.
#include <QtGui/QApplication>
#include <QThread>
int zmain(int argc, char *argv[])
{
QApplication app(argc, argv, true);
app.setQuitOnLastWindowClosed(false);
QThread powerThread;
Power p;
p.moveToThread(&powerThread);
powerThread.start();
return app.exec();
}
// The following code is taken from the wait(2) man page and has been modified to run
// our Qt main() above in a child process. When the child terminates, it is automatically
// restarted.
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int createChild(int argc, char *argv[]) {
pid_t cpid, w;
int status;
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Code executed by child */
fprintf(stderr, "Child PID is %ld\n", (long) getpid());
exit(zmain(argc, argv));
} else { /* Code executed by parent */
do {
w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
if (w == -1) {
perror("waitpid");
return(EXIT_FAILURE);
}
if (WIFEXITED(status)) {
fprintf(stderr, "exited, status=%d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
fprintf(stderr, "killed by signal %d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
fprintf(stderr, "stopped by signal %d\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
fprintf(stderr, "continued\n");
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
if (WIFEXITED(status) && WEXITSTATUS(status) == 111)
return 111;
return EXIT_SUCCESS;
}
}
int
main(int argc, char *argv[])
{
while (111 != createChild(argc, argv)) {
}
}

Resources