Load Text From File on Drop in Qt

Drag and drop is probably one of my favorite way of interacting with an interface. It’s just intuitive and works nicely. Implementing custom dragging and dropping events isn’t hard at all, as you can easily leverage the power of the Qt Event system with custom filters to customize behavior.

In this particular example, we’ll use a sub-classed version of a QPlainTextEdit and reimplement the Drop Event virtual protected method. If the user drags a text file on it, we want to open the file and output the text to the widget. Now, you could probably do this with a custom event filter, but this is a tutorial and really shouldn’t be taken literally for use. I’ll probably go over installing event filters another time, but sub-classing is an important skill to learn–especially in Qt for customizing behavior!

Before we get to the code, lets go over what happens. When you drag and drop something, several events are triggered. You can customize every aspect of it. For instance, when you are dragging a file over your application’s window, you could draw attention to the drop zone or something along those lines. However, we are mainly interested in the event when the user drops a file on our widget.

Also note that I disabled drag and drop for everything but the entry line edit. In addition, when you create your custom class, make sure you promote the widget in Qt Creator!

Qt Creator Promote Widgets Dialog

Assign your custom widgets for promotion in Qt Creator.

Promote To Context Menu

Use the custom widget to promote your existing widgets.

protected:
    virtual void dropEvent(QDropEvent *event);

 

void EntryTextEdit::dropEvent(QDropEvent *event)
{
    if (event->mimeData()->hasText())
    {
        // Strip the file:/// from the beginning. We want a path--not URL
        // Note that there are other ways to do this, but this is just
        // what I chose to do in this case.
        QFile file (event->mimeData()->text().remove("file:///"));

        // Let the user know that they should only be loading text files.
        // Make sure to ignore the case (.txt and .TXT...)
        if (!file.fileName().endsWith(".txt", Qt::CaseInsensitive))
        {
            QMessageBox::critical(this, "Pig Latin Translator",
                                  "File must be a text file.");
            return;
        }

        // Couldn't open the file for reading
        if (!file.open(QIODevice::ReadOnly))
        {
            QMessageBox::critical(this, "Pig Latin Translator",
                                  "Could not load file for translation: \n" +
                                  file.errorString());
            return;
        }

        // Initalize a text stream with the file IODevice and
        // read all text to the widget.
        // Note that this could be unsafe in a production application,
        // since it doesn't check for massive files (e.g. 150MB text file).
        QTextStream in(&file);
        this->setPlainText(in.readAll());
    }
}

The main thing you should worry about is that you load the file with a text stream (see line 32, 33). This is the best way I found to get the text from the file. You could go about it in other ways, but I stuck with this for the example. Also, the important thing to remember is that you do not need to re-implement an entire widget to provide custom functionality such as this. I cannot believe I spent at least a month with Qt while learning, forgetting basic C++ inheritance principles…

That about wraps this up. It’s not as hard as you might think to do something like that. If you are interested in doing this without subclassing everything, I would encourage you to look at a custom event filter. 😉

Leave a comment