Expensive operation on Drag and Drop
-
Hi, I have a desktop operation written on C++/Qt.
When Items are being dragged from my Application's main window to some other application I need to perform an expensive time consuming operation on these items (decrypt the data of those items).
I implemented these operations in the mouseMoveEvent, and the application window becomes blocked rignt whed the dragging starts.What I want to do is to perform these operations when the dragging is finished, i.e. when the user releases the mouse button above some other application's window.
But seems like this is not really possible because I need to finish decrypting data to call drag->setMimeData() before the dragging starts. -
Hi,
welcome. So the problem, that GUI operations can trigger computations, which block the Qt Event queue for long enough to make the window freeze is a general one to which the most basic answer is of cause threading.
In Qt threading is not as enervating and error prone as you might be used to (see "here":http://qt-project.org/doc/qt-4.8/threads-qobject.html for details). The general idea is that any QObject has an owning QThread. Each QThread runs an event queue. So when you have a GUI operation that triggers a costly operation, I would
write a QObject subclass that performs this operation within a slot
then make an instance of it
make a QThread instance and call start on it
"move":http://qt-project.org/doc/qt-4.8/qobject.html#moveToThread the computation object to the thread
connect the gui signal of the triggering gui operation to a slot of your computation object, which starts the computation. This connection will be a "QueuedConnection":http://qt-project.org/doc/qt-5/qt.html#ConnectionType-enum automatically.
However packing the results into a MimeData and sending it is a differnt issue. I must admit that I never actually used Mime data. Maybe somene else can help here?!?
Best Soraltan
-
Thank you for your answer, Soraltan!
The problem is that the current implementation of QDrag class requires that the data decryption finishes before dragging actually starts because it require calling QDrag::setMimeData() which should accept decrypted into the temporary directory file paths as an argument.
Therefore the GUI thread still needs to wait until the operation completes and there will be a huge lag between the user's attempt to drag an item and the Application's reaction to it even if using threads.- How can I start dragging without knowing the paths of decrypted files?
- How can I get notified about the dragging has finished and supply decrypted file paths to the application that accepted the drag at this moment?
-
Hi,
One example might interest you: Delayed Encoding in the Drag and Drop examples in Qt's documentation.
Hope it helps
-
SGaist,
Sounds promising, thanks a lot! I'll check this article! -
The example SGaist linked is a great way to accomplish this, and likely the best solution. I was writing some sample code up before I saw that example, so I thought I'd post it for you anyways:
Widget.hpp
@
#include "Decryptor.hpp"#include <QtWidgets/QWidget>
#include <QtGui/QDrag>class Widget : public QWidget
{
Q_OBJECTpublic:
explicit Widget(QWidget* parent = NULL);
virtual ~Widget();signals:
void doDecrypt(const QString& text, QDrag* drag);public slots:
void processDragRequest(const QString& plainText, QDrag* drag);protected:
void mousePressEvent(QMouseEvent* event);private:
Decryptor* decrypt;
};
@Widget.cpp
@
#include "Widget.hpp"#include <QtCore/QThread>
#include <QtCore/QMimeData>
#include <QtGui/QMouseEvent>Widget::Widget(QWidget* parent) :
QWidget(parent),
{
decrypt = new Decryptor();QThread* decryptThread = new QThread(this);
decrypt->moveToThread(decryptThread);connect(this, &Widget::doDecrypt, decrypt, &Decryptor::doDecrypt);
connect(decrypt, &Decryptor::doneDecrypt, this, &Widget::processDragRequest);decryptThread->start();
}void Widget::processDragRequest(const QString& plainText, QDrag* drag)
{
Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
}void Widget::mousePressEvent(QMouseEvent* event)
{
// Get your cipher text
QString cipherText;QMimeData* mimeData = new QMimeData();
mimeData->setText(cipherText);QDrag* drag = new QDrag(this);
drag->setMimeData(mimeData);emit doDecrypt(cipherText, drag);
}
@Decryptor.hpp
@
#include <QtCore/QObject>
#include <QtGui/QDrag>class Decryptor : public QObject
{
Q_OBJECTpublic:
explicit Decryptor(QObject* parent = NULL);signals:
void doneDecrypt(const QString& plainText, QDrag* drag);public slots:
void doDecrypt(const QString& cipherText, QDrag* drag);
};
@Decryptor.cpp
@#include "decryptor.hpp"
#include <QtCore/QDebug>
Decryptor::Decryptor(QObject* parent) :
QObject(parent)
{}void Decryptor::doDecrypt(const QString& cipherText, QDrag* drag)
{
for (int i = 0; i < 100000; ++i)
{
qDebug() << i;
}QString decryptedText = "decryptedText";
emit doneDecrypt(plainText, drag);
}
@