From: Brian Vanderburg II on 18 Apr 2008 02:16 I've got a quick question about developing a portable applications. By portable I mean like portable-apps, can run without being installed and without 'touching' user account folders. What is the best way of doing this in wxWidgets. In my application I currently always create a file configuration and use wxStandardPaths. For now I'm testing out this approach: Derive from wxStandardPaths a custom AppPaths object, and override the needed GetDataDir/GetUserDataDir/etc, and have a custom GuiAppTraits return the new paths object. By default they simply call wxStandardPaths::GetDataDir, but if a portable mode flag is set it will determine the executable path and solve for a data directory/user data directory like that instead. I may need to override others to get localizations working. Also, create a configuration file with wxFileConfig in the user data directory by directly specifying the path to the configure file. Also, what is a good way to detect portable mode besides using a 'command line' flag 'myapp --portable' or 'myapp /portable', which would work but require the user to execute it from a command line interface. B. Vanderburg II
From: Julian Smart on 18 Apr 2008 03:33 Hi Brian, I've done this with an application (Writer's Cafe 2, currently in beta) and I didn't override wxStandardPaths, I have my own set of functions (that use wxStandardPaths for the non-portable case). I also handle path localization in my own functions, mostly relative to the executable path or application data path. I use a local wxFileConfig if in portable mode, as per your method. To set and detect portable mode, I do the following. From my Mobile Preferences dialog, the user can run a wizard that copies the program to the external drive and creates a small configuration file (also using wxFileConfig) next to the executable. The configuration file tells the app where the actual settings are located, relative to the drive root. Now when the user starts the app from the mobile drive, it deduces the drive location from the executable location and then the regular settings are loaded from a file in the Application Data folder on the mobile drive. So from that point on only temporary files need be created on the fixed drive. When writing MRU files and other data paths to settings (registry or file), I convert them to paths relative to the current external drive if appropriate. I also save the name of the drive so that when reading settings, all files can be converted back to the correct paths for the _current_ drive name. This ensures that the paths still work if the drive changes (and if the operating system changes!) My mobile setup wizard can copy the app on any of the 3 major platforms from itself (where it's currently installed), from a set of zip files on local disk, or from the latest version on the web site (again in zip form to make it easy to unpack and copy after it automatically downloads the file). Having the installation done by the app reduces the opportunity for things to end up in the wrong place, although the downside is you need at least one conventional installation of the app. I use the structure: /Applications/<AppName>/<Platform>/<App Folder> though equally you could have /Applications/<Platform>/<AppName>/<App Folder> or something different. On Windows, the wizard creates a shortcut on the root of the drive, and on Linux it creates a script which has the same effect. I haven't been able to create a shortcut on Mac OS. Note that on Linux you may need to add something like this to /etc/fstab to allow apps to run from the drive: /dev/sdb1 /media/usbdisk vfat rw,shortname=mixed,uid=1000,gid=1000,user,auto,exec 0 0 otherwise nothing shows up as executable (e.g. on a FAT32-formatted drive). I also have a mode whereby the app is aware of a mobile drive, but the program itself doesn't have to be run from the drive. I wouldn't claim this is all trivial to do - especially the wizard and installation onto the drive - but once done it's a nice capability to have. Best regards, Julian Brian Vanderburg II wrote: > I've got a quick question about developing a portable applications. > By portable I mean like portable-apps, can run without being installed > and without 'touching' user account folders. > > What is the best way of doing this in wxWidgets. In my application I > currently always create a file configuration and use wxStandardPaths. > For now I'm testing out this approach: > > Derive from wxStandardPaths a custom AppPaths object, and override the > needed GetDataDir/GetUserDataDir/etc, and have a custom GuiAppTraits > return the new paths object. By default they simply call > wxStandardPaths::GetDataDir, but if a portable mode flag is set it > will determine the executable path and solve for a data directory/user > data directory like that instead. I may need to override others to > get localizations working. Also, create a configuration file with > wxFileConfig in the user data directory by directly specifying the > path to the configure file. > > Also, what is a good way to detect portable mode besides using a > 'command line' flag 'myapp --portable' or 'myapp /portable', which > would work but require the user to execute it from a command line > interface. > > B. Vanderburg II > > > _______________________________________________ > wx-users mailing list > wx-users(a)lists.wxwidgets.org > http://lists.wxwidgets.org/mailman/listinfo/wx-users > > -- Julian Smart, Anthemion Software Ltd. 28/5 Gillespie Crescent, Edinburgh, Midlothian, EH10 4HU www.anthemion.co.uk | +44 (0)131 229 5306 Tools for writers: www.writerscafe.co.uk wxWidgets RAD: www.anthemion.co.uk/dialogblocks
From: klaas.holwerda on 18 Apr 2008 04:40 Brian Vanderburg II wrote: > I've got a quick question about developing a portable applications. > By portable I mean like portable-apps, can run without being installed > and without 'touching' user account folders. In my application i always use two environment variables, one MyApp_ROOT and one HOME or User_Settings. Sometimes a third for common application libraries/data. The application is then started via a bat or shell file, settings those variables to anywhere the user/installer wants them to be. Even wxFileConfig and wxStandardPaths can be used with those variables available in the applycation. All big applications (CAE CAD) work like this. In any case NEVER do anything close to storing into the registry, portable or not its a nightmare. Now in the above case, you can set the variables MyApp_ROOT etc. during the installation phase, making the myapp.bat.sh file ready to start. Of course the value can also be detected inside your app, finding the location of it on the current drive/memory stick. The main thing is that the user/maintainer of the application has the freedom to setup your application location they he wants, and is not forced to do it they way you think he wants. Like we prefer to put all applications on the central application server, also a sort of portable, but the location is fixed, while the user changes location/computer, and so has data files, although they are often stored on a central disk too. For most windows application this is already a real nightmare to do. While on Linux/Unix this is in general very easy. Being this the main part of my job, i am very picky on how applications are set up :-) Hope it helps, Klaas
From: Brian Vanderburg II on 18 Apr 2008 06:55 Thanks for the reply. That sounds pretty good. The only app I've currently tried such on detects portable mode by executable name. If it contains 'port', then it runs in portable mode, and an NSIS installer gives the option to install in 'normal' or 'portable' mode, but different versions can't share data (linux version and msw version would have different user configuration files) My normal application startup goes something like: 1. Set app info (app name, vendor name) 2. Create user directories if needed 3. Create configuration object 4. Etc Seems like from this a good approach might to insert '2. Detect portable settings', which would set a flag if portable mode is detected or not, and read in information needed from the portable mode file in the executable directory. App::DetectPortableSettings() { m_portable = false; m_exeDir = GetExecutableDir() (using wxStandardPaths::GetExecutablePath()) if(::wxFileExists(m_exe + "portable.conf")) { m_portable = true; // Find drive location that executable is on m_driveLocation = .. wxFileConfig blah(...) // Read location of real settings/app paths relative to discovered drive location // and set up other directory variables } } App::GetDataDir() { if(m_portable) return m_exeDir + "data"; else return wxStandardPaths::GetDataDir() } App::GetUserDataDir() { if(m_portable) return m_userDataDir; else ... } But how do you determine the drive location from an executable. Such as "K:\blah\blah.exe' is easy since the system has a volume, but '/mnt/media/blah' is not so easy, perhaps scanning fstab or something. Also, even on windows, a drive location may not be a drive letter if the user mounts it as a folder or junction point as is possible for NTFS, but more likely many users would not do that. Brian Vanderburg II Julian Smart wrote: > Hi Brian, > > I've done this with an application (Writer's Cafe 2, currently in > beta) and I didn't override wxStandardPaths, I have my own set of > functions (that use wxStandardPaths for the non-portable case). I also > handle path localization in my own functions, mostly relative to the > executable path or application data path. > > I use a local wxFileConfig if in portable mode, as per your method. To > set and detect portable mode, I do the following. > > From my Mobile Preferences dialog, the user can run a wizard that > copies the program to the external drive and creates a small > configuration file (also using wxFileConfig) next to the executable. > The configuration file tells the app where the actual settings are > located, relative to the drive root. Now when the user starts the app > from the mobile drive, it deduces the drive location from the > executable location and then the regular settings are loaded from a > file in the Application Data folder on the mobile drive. So from that > point on only temporary files need be created on the fixed drive. > When writing MRU files and other data paths to settings (registry or > file), I convert them to paths relative to the current external drive > if appropriate. I also save the name of the drive so that when reading > settings, all files can be converted back to the correct paths for the > _current_ drive name. This ensures that the paths still work if the > drive changes (and if the operating system changes!) > > My mobile setup wizard can copy the app on any of the 3 major > platforms from itself (where it's currently installed), from a set of > zip files on local disk, or from the latest version on the web site > (again in zip form to make it easy to unpack and copy after it > automatically downloads the file). Having the installation done by the > app reduces the opportunity for things to end up in the wrong place, > although the downside is you need at least one conventional > installation of the app. I use the structure: > > /Applications/<AppName>/<Platform>/<App Folder> > > though equally you could have > > /Applications/<Platform>/<AppName>/<App Folder> > > or something different. On Windows, the wizard creates a shortcut on > the root of the drive, and on Linux it creates a script which has the > same effect. I haven't been able to create a shortcut on Mac OS. > > Note that on Linux you may need to add something like this to > /etc/fstab to allow apps to run from the drive: > > /dev/sdb1 /media/usbdisk vfat > rw,shortname=mixed,uid=1000,gid=1000,user,auto,exec 0 0 > > otherwise nothing shows up as executable (e.g. on a FAT32-formatted > drive). > > I also have a mode whereby the app is aware of a mobile drive, but the > program itself doesn't have to be run from the drive. > > I wouldn't claim this is all trivial to do - especially the wizard and > installation onto the drive - but once done it's a nice capability to > have. > > Best regards, > > Julian > > Brian Vanderburg II wrote: >> I've got a quick question about developing a portable applications. >> By portable I mean like portable-apps, can run without being >> installed and without 'touching' user account folders. >> >> What is the best way of doing this in wxWidgets. In my application I >> currently always create a file configuration and use >> wxStandardPaths. For now I'm testing out this approach: >> >> Derive from wxStandardPaths a custom AppPaths object, and override >> the needed GetDataDir/GetUserDataDir/etc, and have a custom >> GuiAppTraits return the new paths object. By default they simply >> call wxStandardPaths::GetDataDir, but if a portable mode flag is set >> it will determine the executable path and solve for a data >> directory/user data directory like that instead. I may need to >> override others to get localizations working. Also, create a >> configuration file with wxFileConfig in the user data directory by >> directly specifying the path to the configure file. >> >> Also, what is a good way to detect portable mode besides using a >> 'command line' flag 'myapp --portable' or 'myapp /portable', which >> would work but require the user to execute it from a command line >> interface. >> >> B. Vanderburg II >> >> >> _______________________________________________ >> wx-users mailing list >> wx-users(a)lists.wxwidgets.org >> http://lists.wxwidgets.org/mailman/listinfo/wx-users >> >> > >
From: Julian Smart on 18 Apr 2008 08:11 Hi Brian, Brian Vanderburg II wrote: > Thanks for the reply. That sounds pretty good. The only app I've > currently tried such on detects portable mode by executable name. If > it contains 'port', then it runs in portable mode, and an NSIS > installer gives the option to install in 'normal' or 'portable' mode, > but different versions can't share data (linux version and msw version > would have different user configuration files) Right; there should definitely be a 'standard' app data location on the drive that all versions of an app can use. > My normal application startup goes something like: > > 1. Set app info (app name, vendor name) > 2. Create user directories if needed > 3. Create configuration object > 4. Etc > > Seems like from this a good approach might to insert '2. Detect > portable settings', which would set a flag if portable mode is > detected or not, and read in information needed from the portable mode > file in the executable directory. > > App::DetectPortableSettings() > { > m_portable = false; > > m_exeDir = GetExecutableDir() (using > wxStandardPaths::GetExecutablePath()) > if(::wxFileExists(m_exe + "portable.conf")) > { > m_portable = true; > > // Find drive location that executable is on > m_driveLocation = .. > > wxFileConfig blah(...) > // Read location of real settings/app paths relative to > discovered drive location > // and set up other directory variables > } > } > > App::GetDataDir() > { > if(m_portable) > return m_exeDir + "data"; > else > return wxStandardPaths::GetDataDir() > } > > App::GetUserDataDir() > { > if(m_portable) > return m_userDataDir; > else > ... > } Yes, this looks good. When in mobile mode I simply set the user data path to <mobile drive>/Application Data/<App Name>. > > But how do you determine the drive location from an executable. Such > as "K:\blah\blah.exe' is easy since the system has a volume, but > '/mnt/media/blah' is not so easy, perhaps scanning fstab or > something. Also, even on windows, a drive location may not be a drive > letter if the user mounts it as a folder or junction point as is > possible for NTFS, but more likely many users would not do that. I use the following to get the available drives: /// Get available drives, optionally only the removable ones bool AnthemionFileUtilities::GetAvailableDrives(wxArrayString& paths, wxArrayString& names, bool removableOnly) { #ifdef __WXGTK__ wxArrayString p; // Enumerate drives in mount directory wxDir dir; if (wxDirExists(wxT("/media"))) AnthemionFileUtilitiesGetDirs(wxT("/media"), p); if (wxDirExists(wxT("/mnt"))) AnthemionFileUtilitiesGetDirs(wxT("/mnt"), p); names.Clear(); size_t i; for (i = 0; i < p.GetCount(); i++) { // Filter out non-removeable drives if (!removableOnly || (/* p[i].Find(wxT("floppy")) == wxNOT_FOUND && */ p[i].Find(wxT("cdrom")) == wxNOT_FOUND)) { paths.Add(p[i]); names.Add(p[i]); } } #else // From dirctrlg.cpp extern size_t wxGetAvailableDrives(wxArrayString &paths, wxArrayString &names, wxArrayInt &icon_ids); wxArrayString paths1, names1; wxArrayInt iconIds; wxGetAvailableDrives(paths1, names1, iconIds); size_t i; for (i = 0; i < paths1.GetCount(); i++) { #ifdef __WXMSW__ if (!removableOnly || (::GetDriveType(paths1[i]) == DRIVE_REMOVABLE)) #endif #ifdef __WXMAC__ // Don't include root path if we're just looking at removeable drives if (!removableOnly || (paths1[i] != wxT("/"))) #endif { paths.Add(paths1[i]); names.Add(names1[i]); } } #endif return true; } and then this to use the available drives to extract the volume from a filename: /// Try to get a volume (drive) from the filename, return true if successful. bool AnthemionFileUtilities::GetVolumeFromFilename(const wxString& filename, wxString& volume) { #ifdef __WXMSW__ volume = wxFileName(filename).GetVolume(); if (volume.Length() == 1) volume += wxT(":"); return !volume.IsEmpty(); #else // Try matching first part of the filename wxArrayString drives, names; GetAvailableDrives(drives, names); // Find the largest volume that matches wxString lastVol; size_t i; for (i = 0; i < drives.GetCount(); i++) { wxString substr(filename.Left(drives[i].Length())); if (substr != wxT("/") && substr == drives[i]) { if (lastVol.IsEmpty() || substr.Length() > lastVol.Length()) { volume = drives[i]; lastVol = volume; } } } return volume.Length() > 0; #endif } I'm sure these could be improved but they work reasonably. If it still fails to find the drive, I show a dialog prompting for the drive. Regards, Julian
|
Pages: 1 Prev: using wxScrolledWindow with wxSimpleHtmlListBox Next: Build problems (SVN HEAD / wxMac) |