I'm porting a c++ Qt application from Windows to OSX and cannot wrap my head around the .app bundle concept. I hope someone can help me understand.
My executable lives here: MyProgram.app/Content/MacOS/MyProgram.exe
My resource folder lives here: MyProgram.app/Content/Resources/
In my code I use a relative path to reference items in the resource folder:
"../Resources/something.png"
This works great if I open the .app bundle and run the .exe directly.
But that is not how the .app bundle is meant to work. The user is supposed to click on the .app bundle in the Finder to run the program. But in that case my relative path no longer works, and this is what I don't understand.
Does anyone understand my problem and how I can fix it?
QApplication::applicationDirPath()
http://doc.qt.io/qt-5/qcoreapplication.html#applicationDirPath
We use:
QDir
CoreDir::bundle()
{
// Trolltech provided example
CFURLRef appUrlRef = CFBundleCopyBundleURL( CFBundleGetMainBundle() );
CFStringRef macPath = CFURLCopyFileSystemPath( appUrlRef, kCFURLPOSIXPathStyle );
QString path = CFStringToQString( macPath );
CFRelease(appUrlRef);
CFRelease(macPath);
return QDir( path );
}
So do CoreDir::bundle().filePath( "../Resources" );
When you compile your product, have your tried setting the path of Resources to be relative? Otherwise, you can retrieve the main bundle, the URL of the app thereof and append it to the Resources URL.
Bundle Programming Guide
There's a manual for everything, it seems :)
Related
I am trying to open a pdf manual for some hardware from within my application. It will be triggered from the help menu. I can't seem to get my application to open a file from a relative path using the OS default application. I found
QDesktopServices::openUrl(QUrl("file:///home/folder/Manual.pdf"));
referenced on the Qt forums and this works for me except I am going to install my software on another computer and the absolute path won't be the same. I'd like to install the manual next to my application and open it with something like
QDesktopServices::openUrl(QUrl("file:///Manual.pdf"));
but this fails to open with
ShellExecute 'file:///Manual.pdf' failed (error 2).
Has anyone done this before?
You can retrieve the absolute path with
QString a = QFileInfo("Manual.pdf").absoluteFilePath();
then pass it to QUrl, this way:
QUrl url = QUrl::fromLocalFile(a);
If Manual.pdf is in the folder of the executable, use:
QDesktopServices::openUrl(QUrl("file:///" + QApplication::applicationDirPath() + "/Manual.pdf"));
If you later decide to put the pdf in a dedicated sub-folder, e.g. doc, in the code above replace /Manual.pdf with /doc/Manual.pdf.
I'm writing a cross-platform C++ program using Qt and I want to package/embed a number of binary executables within the program. The program should be able to execute these binaries at runtime.
I figured, I would need QResource and QProcess using start() and the ":/..." notation, but I don't seem to get the process running. Is there anything I am missing? Should it work like this? Does the binary need to be set as executable?
Background: I am writing a tool which uses Git and I don't want to require the end-user to install Git manually.
(Trying this on Mac OS X, BTW.)
Update:
I am using the following code (C++, Qt on Mac OS X):
QString program = ":/git";
QStringList arguments;
arguments << "help" << "commit";
myProcess->start(program, arguments);
The Git executable is in the project path, my resources.qrc references it like so:
<qresource prefix="/">
<file>git</file>
</qresource>
I'm not getting an error, but the program is not executed. Behavior is the same when I set program to a non-existing file. Replacing ":/git" by the absolute path to git works perfectly.
You can't execute a program directly from a resource.
If you had a program in a resource and you wanted to execute it, you'd first have to read it out of the resource, write it to a file, make the file executable, then execute it.
Also, when you say that you're not getting an error, that probably means that you aren't checking for errors properly.
Several years late, but the question is still relevant. I had the same problem when wanting to embed rclone.
In the .pro file, add
# From http://stackoverflow.com/a/37561981
defineReplace(copyToDir) {
files = $$1
DIR = $$2
LINK =
for(FILE, files) {
LINK += $$QMAKE_COPY $$shell_path($$FILE) $$shell_path($$DIR) $$escape_expand(\\n\\t)
}
return($$LINK)
}
defineReplace(copyToBuilddir) {
return($$copyToDir($$1, $$OUT_PWD))
}
# Copy the binary files dependent on the system architecture
win32 {
message("Windows")
QMAKE_POST_LINK += $$copyToBuilddir($$PWD/rclone/windows/rclone.exe)
}else: unix:!macx {
message("Linux")
QMAKE_POST_LINK += $$copyToBuilddir($$PWD/rclone/linux/rclone)
}else: macx: {
# Here we want to place the binaries inside the application bundle, so the
# QMAKE_POST_LINK approach will not work because it places them in the same
# directory as the bundle and not inside it. Instead, use QMAKE_BUNDLE_DATA.
message("macOS")
MediaFiles.files += $$PWD/rclone/macOS/rclone
MediaFiles.path = Contents/MacOS
QMAKE_BUNDLE_DATA += MediaFiles
}
Notice how there are slight differences for each platform, but in general the approach is the same.
qmake will copy this files to the destination directory, and they will be accessible by simply making the process call to the local relative directory.
In the following code, I call the executable from QML, and it's going to be very similar in C++ as well:
var rcloneCommand
if (Qt.platform.os === "windows") {
console.log("Windows")
rcloneCommand = "rclone.exe"
} else {
console.log("OSX/Linux")
rcloneCommand = "./rclone"
}
qProcess.start(rcloneCommand, ["--config", "rclone.conf", "-v", "copy", "--stats", "1s", source, destination]);
I don't think resources would work. Processes are created by operating system, and resources are handled by application.
One possible solution would be bundle additional executables somewhere in your application directory and execute them from that path.
So the problem doesn't seem to be extracting the git executable from the resource so much as executing it?
The git program is generate don disk correctly, can you check it's permissions ?
i have made a MFC-based game and my project includes images and sounds. Now i want to create an installer setup in order to distribute it.
i have used the resources by providing their exact path in e.g
img->Load(L"C:\\Users\\Ad33l's\\Desktop\\block mania\\block mania\\res\\db.png");
MCIWndCreate(NULL, NULL,WS_POPUP|MCIWNDF_NOPLAYBAR|MCIWNDF_NOMENU,L"C:\\Users\\Ad33l's\\Desktop\\block mania\\block mania\\res\\tick.wav");
1.Can someone tell me any way to avoid Hard-coding the actual resource path as these resource files will not be present at this same exact path in other computers ?
2.Also guide me to handle these resource files during the creation of standalone SETUP (i am using advance installer )
(as an actual answer).
Do not use absolute path, always use relative path; relative to your EXE file is one solution.
The EXE path can be found using GetModuleFileName.
char appPath[MAXFILENAMELEN];
GetModuleFileName(NULL, appPath, MAXFILENAMELEN);
(addendum) appPath name is misleading, it contains the full name of the application; you need to extract the path from the full application name.
We do something like this:
(edit to make it compilable in unicode)
TCHAR applicationPath[MAX_PATH];
GetModuleFileName(NULL, applicationPath, MAX_PATH);
CString sSoundFile(applicationPath);
PathRemoveFileSpec(sSoundFile.GetBuffer());
sSoundFile.ReleaseBuffer();
From there, you can do something like (pseudocode-ish):
img.Load( appPath + "//Images//db.png" );
You can have a variable that saves the directory they want to install your program in. After they choose the directory they want to install it in, go off of that. Or you can also use system folders like the appdata folder
A first solution would be to configure your setup project to install the installation files under the DesktopFolder\block mania\block mania\res. Then, you can access within your application the current user Desktop location and append to it the remaining fix location (block mania\block mania\res).
Another solution would be to configure the setup project to create registries at install time which will store the paths of the installation files. Then, your application could read the installation paths from registry.
Finally you could also create at install time environment variables containing the paths of the installation file and, then use within your application the environment variables to access the installed files locations.
This is my first time using Xcode and it is appalling to me how completely non intuitive this IDE is. I heard it was better in the past and I really hope it was.
My problem is that the resources my program loads, a data file and an .ini file, it automatically searches for these file in my Home folder. I want for it to search for these files in the Resource folder of the .app. I am using C++ and all of the examples I have found are for Objective-C.
Any idea on how to fix this?
You are probably assuming that, when your app launches, the current working directory of the process is your app's bundle. It isn't. (Nothing to do with Xcode particularly -- that's just how OS X works.)
Typically you would use NSBundle (Objective-C) or CFBundle (C)
to find resources in your app bundle. Since you're using C++, let's use the C API.
To find the URL to a file "myFile.ini" in the Resources directory in your app bundle:
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef url = CFBundleCopyResourceURL(mainBundle, CFSTR("myFile"), CFSTR("ini"), NULL);
UInt8 filePath[PATH_MAX];
if (CFURLGetFileSystemRepresentation(url, true, filePath, sizeof(filePath)))
{
// use your API of choice to open and read the file at filePath
}
Or, to just change the CWD to your app bundle:
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef url = CFBundleCopyBundleURL(mainBundle);
UInt8 bundlePath[PATH_MAX];
if (CFURLGetFileSystemRepresentation(url, true, bundlePath, sizeof(bundlePath)))
{
if (chdir((const char*)bundlePath) == 0)
{
// now the CWD is your app bundle, and you can use relative path names to access files inside it
}
}
I am trying out lua script with C++ in Mac OS X. I was finding a way to make the program returning the current working directory. That's no problem with getcwd, but then I came one thing:
My foo.lua stays at its initial path only. When I compile program, it is not being copied over to the build/Debug directory. Sure, I can grab my script there, but that's just impractical. XCode or any IDE should carry resources to the build zone. XCode does this automatically with iPhone app, but this seems to be a different case. For this case, how to command XCode to put the respective resources in the build directories?
int main (int argc, char * const argv[]) {
...
...
luaL_dofile(luaVM,"/Users/yourNameHere/Desktop/LuaSandbox/LetsTryLua/foo.lua");
//typing the whole absolute path here is just ugly and impractical.
...
...
printf("working directory: %s", buffer);
//output is: working directory: /Users/yourNameHere/Desktop/LuaSandbox/LetsTryLua/build/Debug
...
...
Rather than hard code the path to your Lua script you may want to use the NSBundle API's to find it:
NSBundle * mainNSBundle = [NSBundle mainBundle];
NSString * luaFilePath = [mainNSBundle pathForResource:#"foo"
ofType:#"lua"
inDirectory:NULL
forLocalization:NULL];
luaL_dofile(luaVM,[luaFilePath UTF8String]);
This will find it in the bundle's folder (if you added the "Copy Bundle Resources" build step to your target as the above poster suggested.
Because you're using a .lua file as a resource, I suspect that isn't recognised as a standard resource type and hence it hasn't been automatically copied. You should be able to do this though by adding an extra Copy Bundle Resources build step to your target and then add your file to it in the project view.
If you're creating a command line tool that is not a bundle, then there's never going to be a good solution. If you're creating a regular app then the aforementioned solution will work, but you're going to have to stop assuming that your working directory is set to anything even remotely meaningful at any point in time and use the appropriate methods for finding resources stored within your bundle.