12

As I understand it, the way to packages non-code resources such as data files in a Qt app is using the resource system. However, what if I want to access a resource using a non-Qt function. For example, I may have a .txt or .csv file with some application data that I want to accessing using ifstream. It doesn't seem to work to use the ": ..." syntax in place of a filename for non-Qt functions and classes. Is there a separate workflow for packaging data used by non-Qt functions in an app?

I'm using OSX, but I would assume these issues are platform independent.

daj
  • 6,962
  • 9
  • 45
  • 79
  • possible duplicate of [How to use a QFile with std::iostream?](http://stackoverflow.com/questions/5204335/how-to-use-a-qfile-with-stdiostream) – HostileFork says dont trust SE Oct 20 '14 at 21:05
  • Since Qt resources are compressed into the executable itself the only way to make this work is to extract the file into a temporary folder so that non-Qt code can use it. – sjdowling Oct 20 '14 at 21:06
  • Write a function or class that bridges Qt and non-Qt code. Then call that function (invoke the methods of that class) from non-Qt code. – Siyuan Ren Oct 21 '14 at 00:44
  • @HostileFork Not really, since the resources have nothing to do with `QFile`. Even using a `QIODevice` to `iostream` bridge would be going a long way around the problem. – Kuba hasn't forgotten Monica Oct 23 '14 at 13:19

3 Answers3

6

The sole purpose of the Qt resource system is to bundle data within the executable itself. If you wish not to integrate the data in the executable, then you simply must not use the resource system.

On mac, if you wish to add "data.txt" from project source to your application bundle, but not to the executable itself, add the following to your .pro file:

mac {
    BUNDLE = $$OUT_PWD/$$TARGET$$quote(.app)/Contents
    QMAKE_POST_LINK += ditto \"$$PWD/data.txt\" \"$$BUNDLE/Resources/\";
}

Given the above project file, use the QCoreApplication::applicationDirPath() for a path useful in getting to the file:

#include <QCoreApplication>
#include <QFile>
#include <QDebug>

int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
   qDebug() << QCoreApplication::applicationDirPath();
   QFile data(QCoreApplication::applicationDirPath() + "/../Resources/data.txt");
   if (data.open(QIODevice::ReadOnly | QIODevice::Text))
      qDebug() << data.readAll();
   return 0;
}

In the above example, the Resources folder has nothing to do with the Qt resource system. It's simply a naming convention in OS X application bundles. We're not using the Qt resource system here.

If you wish to use the Qt resource system and access the resource data directly and not through a QFile, the QResource class provides access to resources that are bundled in the executable.

If the code under your control insists on using ifstream for data input, then it's artificially limited and should be fixed. It should use istream instead, as that class can be backed by anything, not necessarily a file. If it's code that you don't control, you could set up the ifstream on a QLocalSocket.

You can map the constant QResource::data() to an input stream via a stream buffer.

If the resource isCompressed(), then you need to first decompress it to a temporary area. You can also disable resource compression to avoid the decompression step. You can use a whole-executable compressor like upx instead - by the time your code runs, everything will be already decompressed and ready to use.

Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
3

You can copy the resource file into a temporary folder. To do this, use a QTemporaryDir which creates a temporary folder and deletes it automatically when the program is finished. To access the path of that folder, use the QTemporaryDir::path() method. Here is an example of how you can use it:

#include <QTemporaryDir>    //You need to include this header
QTemporaryDir temporaryDir;
//Copy the resource file into the temporary folder
QFile::copy(":/exampleprefix/examplefile.txt", temporaryDir.path() + "/examplefile.txt");
//Read the file
std::ifstream fileStream(QString(temporaryDir.path() + "/examplefile.txt").toLatin1().data());
//etc
Donald Duck
  • 8,409
  • 22
  • 75
  • 99
0

What about opening the resource file with a QFile object, wrapping this with a QDataStream object, and wrapping this with a boost::iostreams::stream object, which derives from a specialization of std::basic_istream? Sounds complicated, but does not need too many lines of code, see this answer.

Community
  • 1
  • 1
MarkusParker
  • 1,264
  • 2
  • 17
  • 35