How to set QWidget cursor outside the GUI thread - qt

I am working on QT GUI project. In this application I have a QWidget as main window. I make the cursor from data coming from some source. When I set the cursor of widget. It gives me the following error.
QPixmap: It is not safe to use pixmaps outside the GUI thread
My code is as follows
void ImageWindow::setMouseCursor(unsigned char* data,unsigned char* maskbits,unsigned int length,int xHotSpot, int yHotSpot)
{
QBitmap bitmapData;
QBitmap bitmapMaskData;
bitmapData.loadFromData(data,length);
bitmapMaskData.loadFromData(maskbits,length);
this->setCursor(QCursor(bitmapData,bitmapMaskData,xHotSpot,yHotSpot));
this->update();
}
Function setMouseCursor is called from other class, which set the data of cursor.
ImageWindow is my customized QWidget class.

Apparently, the object which calls setMouseCursor lives outside the GUI thread as far as i can tell. In order to avoid this, make setMouseCursor a slot. Do not call the slot directly, instead emit a signal from the caller object, and connect that signal to setMouseCursor slot using Qt::QueuedConnection.
See : ConnectionType

Two problems:
don't use a QBitmap outside the GUI-thread
don't call gui objects setCursor outside the GUI-thread
Creating a Paint Device
One advantage of using QImage as a
paint device is that it is possible to
guarantee the pixel exactness of any
drawing operation in a
platform-independent way. Another
benefit is that the painting can be
performed in another thread than the
current GUI thread.

Related

how to access Qt MainWindow members

In my project, I want to call a variable QImage image generated in the mainwindow.h, in other class files, e.g., called SegSetupDialog.h. Here the image is loaded by clicking a pushbutton in mainwindow.ui, and the SegSetupDialog is a QDialog, which pops up by clicking one pushbutton in mainwindow.ui.
I tried to use a signal-slot connection to send qimage of mainwindow to SegSetupDialog as follows.
For Class MainWindow:
SegSetupDialog *segsetup;
if(image.isNull()==false)
{
emit sendImgData(image);
qDebug()<<"sendImgData emitted!";
if(segsetup==NULL) segsetup = new SegSetupDialog();
connect(this, SIGNAL(sendImgData(QImage)),segsetup,SLOT(getImgData(QImage)),Qt::QueuedConnection);
}
In SegSetupDialog::getImgData
void SegSetupDialog::getImgData(QImage qimage)
{
qImg = qimage;
qDebug()<<"qimage received!";
}
The above connection seems not to work since the qDebug message in getImgData is not printed out. Anyone can help check something wrong with codes, or suggest other methods for accessing image of mainwindow? Thanks!!
You need to do the signal/slot connection before you emit the signal. Connections are done once, usually in the constructor.
However, you should probably do the connect() in the constructor of SegSetupDialog instead of the MainWindow one. It's SegSetupDialog that wants to be notified about image data updates, so you should establish the connection there.
Also, to make sure the signals and slots are specified correctly, don't use Qt 4 connect() calls. Use Qt 5 ones that are checked at compile-time:
connect(this, &MainWindow::sendImgData,
segsetup, &SegSetupDialog::getImgData, Qt::QueuedConnection);
(Of course change it appropriately if you move it to the SegSetupDialog constructor.)

Is there any QML window UI fully constructed signal?

I've found this, but it is only about QWidget based project. What about ApplicationWindow components in QML?
Finally I've connected to frameSwapped signal of the main top-level QQuickWindow of my application. It is called right after each repainting is done. So, after first repainting, my slot would be called, and I will start to really load the data (what is rather slow). Inside this slot I'm destroying this connection, so I don't slow down the application.
//main.cpp
QQuickWindow* mainWindow =
qobject_cast<QQuickWindow*>(engine.rootObjects().first());
QMetaObject::Connection loadingFinished =
QObject::connect(mainWindow, SIGNAL(frameSwapped()),
&controller, SLOT(construct()));
controller.setConnection(loadingFinished);
//Controller.cpp
void Controller::construct() // this is slot
{
// some really long operation
disconnect(*m_loadingFinished);
}
Hope it will be helpful for someone.

Using QPainter with QPaintDevice multiple times

we all know this Warning from
bool QPainter::​begin(QPaintDevice * device)
Warning: A paint device can only be painted by one painter at a time.
http://doc.qt.io/qt-5/qpainter.html#begin
But what if I have two object sharing one pixmap, and one object Bar contains other object Foo.
class Foo
{
public:
QPixmap* barPixmap;
void draw()
{
QPainter painter(barPixmap);
painter.drawText(0,0,"FooText");
}
}
class Bar
{
public:
QPixmap* barPixmap;
Foo* fooObject;
}
and I got something like this
Bar::paintEvent(QPaintEvent* )
{
QPainter painter(barPixmap);
painter.drawText(50,50,"BarText");
fooObject->draw();
}
Is it multiple drawing? Compiler throws nothing and code seems working.
The warning tells you about creating multiple QPainters at one time. Since all paint events are processed in the main thread, they are processed consequently. As long as QPainter object is destroyed at the end of your event handler, the warning will not appear. Multiple consequent paintings on one device is fine.
However the architecture is questionable. For instance, if multiple widgets are painted this way, one of widgets will display old version of pixmap while second widget will display updated version. This inconsistency can be a problem. Putting any logic in paint event handlers is generaly pointless (and sometimes harmful). You should change pixmap when available data changes and just paint it in the paint event.

How to call plain function from exec()?

I have 2 classes: one maintains some loop (at leas for 2-3 minutes; and is inherited from QObject) and another shows up a progress dialog (inherited from QDialog).
I want to start the loop as soon as the dialog is shown. My first solution was:
int DialogClass::exec()
{
QTimer::singleShot(0, LoopClassPointer, SLOT(start()));
return __super::exec();
}
There is a problem with throwing exceptions from slots. so I considered a possibility to make public slot start() just a public function. But now I don't know how to make it works well. Things like this:
int DialogClass::exec()
{
LoopClassPointer->start();
QApplication::processEvents();
return __super::exec();
}
don't help. The dialog doesn't appears.
Is there a common approach to this kind of situations?
some details, according to questions:
I have to work with system with its own styles, so we have a common approach in creating any dialogs: to inherit them from stytle class, which is inherited from QDialog.
my 'LoopClassPointer' is an exported class from separate dll (there is no UI support in it).
I have a 'start' button in main app, which connected with a slot, which creates progress dialog and 'LoopClassPointer'. at the moment I send 'LoopClassPointer' instance in the dialog and don't whant to make significant changes in the architecture.
Take a look at QtDemo->Concurrent Programming->Run function
e.g. in Qt 4.8: http://qt-project.org/doc/qt-4.8/qtconcurrent-runfunction.html
In this situation, I recommend you separate the logic of the loop from the dialog. Gui elements should always be kept separate.
It's great that your worker class is derived from QObject because that means you can start it running on a separate thread: -
QThread* m_pWorkerThread = new QThread;
Worker* m_pWorkerObject = new Worker; // assuming this object runs the loop mentioned
// Qt 5 connect syntax
connect(m_pWorkerThread, &QThread::started, m_pWorkerObject, &WorkerObject::start);
connect(m_pWorkerThread, &QThread::finished, m_pWorkerThread, &QThread::deleteThis);
m_pWorkerObject->moveToThread(m_pWorkerThread);
m_pWorkerThread->start();
If you're not familiar with using QThread, then start by reading this.
The only other thing you require is to periodically send signals from your worker object with progress of its work and connect that to a slot in the dialog, which updates its display of the progress.

Ways to create a QDialog outside the main thread

I am trying to create a client application in QT which requires both threads for processing and a variable amount of window instances. But I am having a hard time trying to figure out how to create a new window inside one of the processing thread. I understand that all ui elements must be created in the same thread as the QApplication class, but I need to be able to instantiate, or at least have a reference to a QDialog in another thread.
Communicating between the thread and QDialog can be done using signals, I am not worried about this, but actually creating the window is another matter. I could use signals to tell the main thread to create an instance to the window, and then retrieve the pointer to it somehow, but to me that seems a bit to complicated and ugly. Is there a better way to accomplish such a task? To create a QDialog outside the main thread were the QApplication class exists?
Edit : I have tried the Q_INVOKABLE method but it does not work across threads. I have created a view factory class which can create a QDialog of a type I specify and returns a pointer to it. This class has been instantiated in the main GUI thread and a reference to this class is sent to any worker threads. The problem is that, when a thread invokes the create method from the factory using Qt::BlockingQueuedConnection, the invoke method fails. If I change it to Qt::DirectConnection, the invoke method calls the right create method but in the current thread as the worker thread.
My main function looks like this :
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ViewFactory vFactory;
vFactory.registerCreator(Util::W_CONNECT, new ConnectWindow::ConnectCreator());
ClientApp app;
if(!app.Initialize(&vFactory))
return 0;
app.start();
a.exec();
.............................
}
And my run function from the ClientApp thread looks something like this :
void ClientApp::run()
{
QDialog * tmp = NULL;
QMetaObject::invokeMethod(this->_vFactory, "create", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QDialog*, tmp), Q_ARG(int, 0));
}
Like I said, the invokeMothod will not fail if I change the connection type to Qt::DirectConnection, so the params are not the problem, but rather calling the method across a separate worker thread.
You can only do Gui stuff in the gui thread. The obvious solution is for the worker thread to send a message to the gui thread = a signal in Qt terms.
If a worker thread needs to ask a question it should send a message to the gui thread and then block until it gets a signal back.
AFAIK, signals (or just a dynamically callable method, using Q_INVOKABLE) or an event is the way to go.
Note that, using QMetaObject::invokeMethod() (with Qt::BlockedConnection), you can call a function safely across threads and get a return value back without too much coding.
It seems like QObject::moveToThread can solve this problem. This function moves event processing loop to another thread.
Example from Qt documentation:
myObject->moveToThread(QApplication::instance()->thread());

Resources