Qt: send QPixmap in QDrag's QMimeData? - qt

I create a drag object from a QListWidgetItem.
I can send text as mime data in this drag object.
How can I send a pixmap and retrieve it from the mime data?
Would it even be possible to create a QGraphicsItem and retrieve it?
I try to drag & drop from the QListWidget into a QGraphicsView.

There are multiple ways to send a QPixmap through QMimeData:
by encoding it into a file format such as PNG and sending that with mime-type image/png (QMimeData has built-in support for that, cf. QMimeData::imageData()).
by serialising the QPixmap into a QByteArray using a QDataStream and sending the serialisation under an app-specific mime-type application/x-app-name.
by writing the image data to a file on disk and sending a file-URL for it with mime-type text/uri-list (QMimeData has built-in support for this, cf. QMimeData::urls()). This allows to drag these images onto a file manager or the desktop.
similar to (2) above, you can also create a QGraphicsItem, stuff its address into a QByteArray and send that under an app-specific mime-type. This doesn't work if the drag ends in another process, of course (the receiving site can test, because QDragEvent::source() returns 0 in that case), and it requires special care to handle the graphic item's lifetime.
Seeing as QMimeData allows you to pass several formats at once, these options are non-exclusive. You should, however, sort the formats you return from your reimplementation of QMimeData::formats() in order of decreasing specificity, i.e. your app-private formats come first, and text/uri-list comes last.

Related

Save UI settings with QSettings or QJson?

Saving UI settings with QSettings is cumbersome and buggy, because each time you must use setValue() and value() functions and also define groups, application name and organization which can be buggy in large applications:
QSettings settings(qApp->applicationDirPath() + "/" + qApp->applicationName() + ".ini" , QSettings::IniFormat) ...
settings.beginGroup("someGroup");
settings.setValue("someKey", "blah blah");
QString str = settings.value("someKey");
settings.endGroup();
However with JSON it can be simpler:
QJsonObject obj;
obj["someKey"] = "blah blah"
...
What is the best practice for saving and restoring ui settings?
Save each key/value in QSettings?
Save in QJson and then save with QSettings?
Save in QJson only (with another mechanism for defining groups and application)?
Any other idea?
The QSettings code won't be more cumbersome than your QJsonObject example if you use all benefits of the first one.
Default QSettings constructor:
You can set the application and organization names just once:
QApplication::setApplicationName("My Application");
QApplication::setOrganizationName("My Organization");
QSettings::setDefaultFormat(QSettings::IniFormat);
Then simply use the default QSettings constructor anywhere in your code:
QSettings settings;
settings.setValue("Key", "Value");
Group as an argument:
The settings group for your keys can be set without using the beginGroup() / endGroup() methods. Simply pass the slash-delimited argument to thevalue() / setValue() methods:
settings.setValue("Group/Key", "Value");
Storing the UI settings:
It's not clear from your question what exact UI settings you want to save, however there are two handy methods – QWidget::saveGeometry() and QMainWindow::saveState(). You can use it to store your windows geometry and state respectively:
QSettings settings;
settings.setValue("ui/geometry", saveGeometry());
settings.setValue("ui/state", saveState());
JSON:
In case if you still want some deep nesting and hierarchy for your settings file, you're right, you will have to use JSON. The most convenient way is to register the custom read/write functions using the QSettings::registerFormat. Why still QSettings? This class is designed considering the cross-platform code, no need to reinvent the wheel.
Of course, you can also write your own JSON settings class from scratch. But if there is no need in multilevel settings hierarchy – is it worth it?
In terms of application design you can wrap QSettings in an additional class. In this case you can easily experiment and switch to your own JSON read/write implementations without touching the main code.
Standard system paths:
In your example you are using the applicationDirPath() to store the settings data. That's an improper place to keep your settings for the most applications (e.g. you will likely face the Windows UAC issues in this case; Unix systems also have the separate directory for such files). Use the paths intended by the operating system for storing application data.
For example, on Windows QSettings::setDefaultFormat(QSettings::IniFormat) coupled with the default scope (UserScope) will store the settings in the %APPDATA% path. This also improves the cross-platform portability of your code.

In Qt, how to setCodecForCStrings globally (in header)?

I'm developing a bunch of Qt applications in C++ and they all use some modules (translation units) for common functionality that use Qt as well.
Whenever I convert a C string (implicit conversion) or C++ string object (fromStdString()) to a QString object, I expect the original data to be UTF-8 encoded and vice versa (toStdString()).
Since the default is Latin-1, I have to set the codec "manually" (in the init procedure of every one of my programs) to UTF-8:
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("utf8"));
Not all of my modules have an init procedure. The ones containing a class do (I can put this line in the class constructor), but some modules just contain a namespace with lots of functions. So there's no place for setCodecForCStrings(). Whenever I convert from/to a QString implicitly (from within one of my modules), I rely on the codec being already set by the init procedure of the main program, which seems to be a rather bad solution.
Is there a reliable way to set the codec to UTF-8 in my modules, or will I just have to be very careful not to use implicit conversions at all (in my modules at least) and write something like std::string(q.toUtf8().constData()) instead of q.toStdString()?
This can be done using a class definition for an automatically instantiated singleton-similar class having some init code in its constructor:
class ModuleInit {
static ModuleInit *instance;
public:
ModuleInit() {
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("utf8"));
}
};
ModuleInit * ModuleInit::instance = new ModuleInit(); // put this line in .cpp!
Putting this code anywhere into any project will set the text codec to UTF-8. You can, however, overwrite the text codec in the main() method, since the code above is executed even before the main() method.
With "anywhere" I of course mean places where it is syntactically allowed to. Put the class definition in some header file, and the instantiation in the appropriate .cpp file.
You can even put the whole code in one .cpp file, say moduleinit.cpp which you compile and link, but never explicitly use (there is no header file you could include...). This will also avoid accidental instantiations except the very first one and you will not have any problems with duplicate class names.
Note that you can't set the codec for C-strings in one particular file. Setting the codec using QTextCodec::setCodecForCString will set it application-wide!

How to read/write a text file in qt?

I have a QPlainTextEdit in my Form and I want to read the whole Resource.txt document which is placed in Other Files of my project and after a timer ticks i want the application save the contents of the QPlainTextEdit in the document.
I know it's a dumb question but I can't find a solution.
QTextStream.readAll() lets you read a file to a QString. This constructor (or the method setPlainText) for QPlainTextEdit lets you set a string displayed in the editor. Use a QTimer to trigger a slot which reads the contents of the QPlainTextEdit into a QString with the toPlainText method after a desired amount of time. Write the result to file using a QTextStream again.
The tutorial Getting Started Programming with Qt takes you through building a text editor, once you have followed this, try adding a QTimer to trigger a save.

Using QTWebKit to display a website stored in memory

Currently I have my HTML, JS, CSS, graphics, etc stored locally on hard disk and access them using QWebFrame::SetUrl( QUrl::fromLocalFile( "appFolder\html\index.html" )). At some point I am going to need to encrypt the locally stored files so I'm looking for a way to either decrypt them as they're requested or to decrypt them all into memory and access them that way.
I know I can use QWebFrame::setContent( htmlData ) to load the HTML from memory so I can load the encrypted HTML file, decrypt it in memory and then display it that way, but how would I go about the other data (JS, CSS, graphics, etc) which is currently stored in subfolders?
Alternatively, is there a way I can intercept requests for access to all the HTML, JS, CSS, etc files and decrypt them as they're loaded?
By using my own NetworkAccessManager I can intercept calls to createRequest so I can see when each file is being loaded, but I can't see how to use this to decrypt the data on the fly. I can also connect a slot function to the finished(QNetworkReply*) signal, but at that point the data has already been read - the QIODevice's current position is pointing to the end of the file.
I'd be very grateful for any advice or pointers in the right direction.
I think in your case the best solution is to inherit QNetworkReply class and use this new class in reimplemented QNetworkAccessManager::createRequest() function.
In general, you should reimplement next virtual functions of QNetworkReply:
bytesAvailable(), readData(char *data, qint64 maxSize), close(), abort().
For example, readData should be the folowing:
qint64 NetworkReplyEx::readData(char *data, qint64 maxSize)
{
return m_buffer.read(data, maxSize);
}
where m_buffer is already decrypted data.
Also you need to add all necessary logic in this class to get encrypted data, decrypt this data...
In the end you should manually emit finished() signal inside new class, so QWebView or other related class will get decrypted html.

FileReference.save() duplicates ByteArray

I've encountered a memory problem using FileReference.save(). My Flash application generates of a lot of data in real-time and needs to save this data to a local file. As I understand, Flash 10 (as opposed to AIR) does not support streaming to a file. But, what's even worse is that FileReference.save() duplicates all the data before saving it. I was looking for a workaround to this doubled memory usage and thought about the following approach:
What if I pass a custom subclass of ByteArray as an argument to FileReference.save(), where this ByteArray subclass would override all read*() methods. The overridden read*() methods would wait for a piece of data to be generated by my application, return this piece of data and immediately remove it from the memory. I know how much data will be generated, so I could also override length/bytesAvailable methods.
Would it be possible? Could you give me some hint how to do it? I've created a subclass of ByteArray, registered an alias for it, passed an instance of this subclass to FileReference.save(), but somehow FileReference.save() seems to treat it just as it was a ByteArray instance and doesn't call any of my overridden methods...
Thanks a lot for any help!
It's not something I've tried before, but can you try sending the data out to a php application that would handle saving the ByteArray to the server, much like saving an image to the server, so then you'd use URLLoader.data instead, using something like this:
http://www.zedia.net/2008/sending-bytearray-and-variables-to-server-side-script-at-the-same-time/
It's an interesting idea. Perhaps to start you should just add traces in your extended ByteArray to see how the FileReference#save() functions internally.
If it has some kind of
while( originalByteArray.bytesAvailable )
writeToSaveBuffer( originalByteArray.readByte() );
functionality the overrides could just truncate the original buffer on every read like you say, something like:
override function readByte() : uint {
var b : uint = super.readByte();
// Truncate the bytes (assuming bytesAvailable = length - removedBytes)
length = length - bytesAvailable;
return b;
}
On the other hand, if this now works I guess the original byte array would not be available afterwards in the application anymore.
(i havn't tested this myself, truncating might require more work than the example)

Resources