Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Make Slicer application fully portable #5029

Merged
merged 1 commit into from
Jan 5, 2021

Conversation

lassoan
Copy link
Contributor

@lassoan lassoan commented Jul 5, 2020

Slicer install tree was portable but not fully self-contained, as settings and extensions were stored in the user profile folder.

Added a new option Slicer_STORE_SETTINGS_IN_HOME_DIR (enabled by default) that makes Slicer store all settings and extensions in the application home folder, within (organizationName) subfolder (as QSettings default constructor saves settings file into (organizationName) subfolder by default), making the application fully portable.

To still allow having settings that are common to all installed application versions for a user (e.g., DICOM database folder, confirmation popup suppressions, ...), the common non-revision-specific is still stored in the user profile directory by default. However, if the user places an (applicationName).ini file in the application home folder/(organizationName) then that file is used instead.

By default, (applicationName)=Slicer and (organizationName)=NA-MIC and therefore the .ini files are: (applicationHome)/NA-MIC/Slicer.ini and (applicationHome)/NA-MIC/Slicer-(revision).ini; and extensions are stored in (applicationHome)/NA-MIC/Extensions-(revision).

re #4966

Prerequisites to merge:

@lassoan lassoan requested a review from jcfr July 5, 2020 01:42
@lassoan
Copy link
Contributor Author

lassoan commented Jul 5, 2020

This would allow using Slicer as a viewer on a DICOM DVD/USB stick, the only thing would need to be fixed for this is to make DICOM database store relative paths for images that are copied into the database (currently all images and thumbnails are stored with absolute paths).

@lassoan lassoan requested a review from pieper July 5, 2020 01:46
@lassoan
Copy link
Contributor Author

lassoan commented Jul 5, 2020

Unfortunately, there are a number of absolute paths in the .ini files that would cause problems (see below). So, this is not a full solution yet. @jcfr what do you think, should we use <APPLAUNCHER_DIR> placeholder in the .ini files, too?


Slicer.ini:

  • DatabaseDirectory_0.6.3=C:/Users/andra/OneDrive/Documents/SlicerDICOMDatabase_2
  • DefaultScenePath=C:/Users/andra/OneDrive/Documents
  • TemporaryDirectory=C:/Users/andra/AppData/Local/Temp/Slicer
  • Path=C:/Users/andra/AppData/Local/Temp/Slicer/RemoteIO

Slicer-NNN.ini:

  • [LogFiles]
  • AdditionalPaths=C:/D/S4D/Slicer-build/NA-MIC/Extensions-29209/BreastImplantAnalyzer/lib/Slicer-4.11/qt-scripted-modules
  • InstallPath=C:/D/S4D/Slicer-build/NA-MIC/Extensions-29209
  • [LibraryPaths]
  • [PYTHONPATH]

@cpinter
Copy link
Member

cpinter commented Jul 5, 2020

I think the variable name Slicer_STORE_SETTINGS_IN_HOME_DIR is misleading. Most people think of the user's folder when they hear "home dir". It would be better to call it Slicer_STORE_SETTINGS_IN_APPLICATION_HOME_DIR or Slicer_STORE_SETTINGS_IN_APP_HOME_DIR or just Slicer_STORE_SETTINGS_IN_APP_DIR.

@pieper
Copy link
Member

pieper commented Jul 5, 2020

I'd very much like to see the application be relocatable. Running Slicer off a CD was the original motivation for having the launcher in the first place (so we could hand out CD at RSNA in like 2002 or something).

I agree with @cpinter and prefer Slicer_STORE_SETTINGS_IN_APP_DIR for clarity.

Making everything relative to the app path makes sense to me. It'll also help sandbox the app for security.

@lassoan
Copy link
Contributor Author

lassoan commented Jul 7, 2020

What to do with .slicerrc.py file? These are the candidate locations for the file:

  • application home directory
  • location specified in SLICERRC environment variable
  • ~/.slicerrc.py

Should we add any more locations (e.g., specify file in Slicer.ini or Slicer-NNN.ini)?
Would the search order above work (application home directory would have the highest priority)?

@pieper
Copy link
Member

pieper commented Jul 7, 2020

I think it would be fine to look in the application directory for a .slicerrc.py and in the user's home directory. I also like the idea of a .slicerrc-NNN.py in case you wanted to do something specific to the version.

If we did put it in the .ini file we could also include a application settings panel entry that allows you to browse to set the location and edit it with a simple text editor (and include a link to the readthedocs with information about what you can put there). I still feel like .slicerrc.py should be used with care, since it could be error prone, but we may as well make it fairly easy to use.

@lassoan lassoan force-pushed the fully-portable-application branch 2 times, most recently from 486a76d to b703328 Compare July 8, 2020 03:04
@jcfr
Copy link
Member

jcfr commented Jul 8, 2020

@jcfr what do you think, should we use <APPLAUNCHER_DIR> placeholder in the .ini files, too?

Instead, we should probably use <APPLAUNCHER_SETTINGS_DIR> (see here for description) for settings value that are common to both the launcher and slicer through the use of the AppLauncherLib (LibraryPaths, PYTHONPATH).

For regular settings only relevant to Slicer (e.g LogFiles, InstallPath, ..,) we should introduce an other special placehoder variable called <SLICER_SETTINGS_DIR>.

We would also need to provide an interface for reading/writing settings and ensure the placeholder is properly handled. This means directly using QSettings() method would not work out of the box.

@lassoan
Copy link
Contributor Author

lassoan commented Jul 8, 2020

We would also need to provide an interface for reading/writing settings and ensure the placeholder is properly handled. This means directly using QSettings() method would not work out of the box.

Using a placeholder would have the advantage that we could automatically detect path variables (now developers need to think about converting to/from relative paths in application settings).

However, prohibiting use of QSettings() class would immediately break many things. Instead, we could make a smooth transition: deprecate usage of QSettings and remove its usage from Slicer core now; then gradually remove its usage from extensions (in 1-2 years).

jcfr
jcfr previously approved these changes Jul 8, 2020
Copy link
Member

@jcfr jcfr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this set of changes.

The implicit meaning of relative path along with the use of the path mapper for the settings panel address all the issues very nicely 👍

Base/Python/slicer/slicerqt.py Show resolved Hide resolved
Base/Python/slicer/slicerqt.py Show resolved Hide resolved
Base/QTCore/qSlicerCoreApplication.cxx Outdated Show resolved Hide resolved
Base/QTCore/qSlicerRelativePathMapper.cxx Show resolved Hide resolved
Base/QTCore/qSlicerRelativePathMapper.h Show resolved Hide resolved
Base/QTCore/qSlicerCoreApplication.h Show resolved Hide resolved
Base/QTCore/qSlicerCoreApplication.h Show resolved Hide resolved
Base/QTCore/qSlicerCoreApplication.h Show resolved Hide resolved
Base/QTCore/qSlicerCoreApplication.h Show resolved Hide resolved
@jcfr
Copy link
Member

jcfr commented Jul 8, 2020

Instead, we could make a smooth transition: deprecate usage of QSettings and remove its usage from Slicer core now;

Anticipating the change and to simplify this:

QString extensionInstallPath = coreApp->toSlicerHomeAbsolutePath(
    coreApp->revisionUserSettings()->value("Extensions/InstallPath").toString());

should we have method like revisionUserSettings() returns qSlicerSettings (or even ctkSettings) object that would take care of calling toSlicerHomeAbsolutePath/toSlicerHomeRelativePath ?

Last, is there any absolute path save in within CTK widgets ?

@lassoan
Copy link
Contributor Author

lassoan commented Jul 8, 2020

should we have method like revisionUserSettings() returns qSlicerSettings (or even ctkSettings) object that would take care of calling toSlicerHomeAbsolutePath/toSlicerHomeRelativePath ?

Yes, if we use a placeholder then we could do this automatically. Unfortunately, QSettings value() and setValue() methods are not virtual. Fortunately, we may be able to intercept and rewrite information in the settings file by registering a custom file format (https://doc.qt.io/qt-5/qsettings.html#registerFormat). The file format could be the default ini file, we would just rewrite the placeholder value in QString and QStringList values. I'm looking into this now.

Last, is there any absolute path save in within CTK widgets ?

You are right. Unfortunately, there are a few:

  • Few classes in PluginFramework: I would not touch these
  • ctkCommandLineParser.cpp: I don't know what it uses settings for, maybe we can leave it as is
  • ctkDICOMBrowser.cpp: we'll have to fix the DICOM browser
  • ctkPathLineEdit.cpp: stores history; it is not a huge issue if history always uses absolute paths, but we can add a method to specify QSettings that should be used instead of the default QSettings (which would use the custom storage format)

@jcfr
Copy link
Member

jcfr commented Jul 8, 2020

Fortunately, we may be able to intercept and rewrite information in the settings file by registering a custom file format (https://doc.qt.io/qt-5/qsettings.html#registerFormat). The file format could be the default ini file, we would just rewrite the placeholder value in QString and QStringList values. I'm looking into this now.

🙏 That would be neat.

Few classes in PluginFramework: I would not touch these

Agreed

ctkCommandLineParser.cpp: I don't know what it uses settings for, maybe we can leave it as is

It is false by default and we are not using this feature in Slicer.

Description of the feature is here

 * <li>QSettings support. Default values for arguments can be read from
 *     a QSettings object.</li>

ctkDICOMBrowser.cpp: we'll have to fix the DICOM browser

Agreed. Could be addressed as part of #4722

ctkPathLineEdit.cpp: stores history; it is not a huge issue if history always uses absolute paths, but we can add a method to specify QSettings that should be used instead of the default QSettings (which would use the custom storage format)

That would be reasonable.

Other approach

I am wondering if this type of settings could be updated explicitly on application shutdown or by providing our own sync method that would call https://doc.qt.io/qt-5/qsettings.html#sync.

Rewriting of settings value using qSlicerCoreApplication::toSlicerHomeRelativePath could be done by:

  • (1) iterating over all settings
  • or (2) only by updating settings explicitly registered within the application

@lassoan
Copy link
Contributor Author

lassoan commented Jul 8, 2020

Unfortunately, value, setValue, and sync methods are not virtual, so we could not reliably override them.

I'm now trying to add pathValue() and setPathValue() methods to ctkSettings to simplify the syntax. Instead of:

QString extensionInstallPath = coreApp->toSlicerHomeAbsolutePath(coreApp->revisionUserSettings()->value("Extensions/InstallPath").toString());

we could then use:

QString extensionInstallPath = coreApp->revisionUserSettings()->pathValue("Extensions/InstallPath").toString()

When CTK classes read/write paths from/to settings, they would try downcasting QSettings to ctkSettings and use pathValue/setPathValue instead of value/setValue.

@jamesobutler
Copy link
Contributor

Isn’t QSettings something at the heart of a QApplication? I use it more so that other Slicer modules for the custom modules I create. It allows for defining user settings for various widgets at the user location and default settings for all user accounts at the system level. Having multiple users using the same application with the same default settings with the ability for user settings is important for me. I often also store and apply presets for various custom modules in this manner. Not sure what I would do if the plan is to phase out the QSettings() usage in the Slicer application. Being fully portable hasn’t been a concern at all for me.

@lassoan
Copy link
Contributor Author

lassoan commented Jul 8, 2020

Slicer has been always using more than just a simple QSettings() for storing application settings (e.g., it uses a common userSettings and revision-specific revisionUserSettings).

Since Python packages must be installed in the Slicer home directory, it cannot be shared between users anymore. Since extensions can depend on various Python packages (with potentially incompatible version requirements), extensions cannot be simply shared between multiple installations of the same Slicer version for the same user. Therefore, we need to make extensions install within the Slicer home directory, too.

Since we have everything in one folder, it makes a lot of sense to go one step further and allow the application to be portable. This opens a up a very important use case: bundling a preconfigured Slicer instance to exported data sets as a viewer. It also allows shared installation for many users in a server environment (as extensions can be all copied into the shared Slicer home directory), and also allows easy distribution preconfigured Slicer environments on training courses.

QSettings() will not go away completely, it will be just simpler and safer to use app->userSettings() and app->revisionUserSettings().

@jcfr
Copy link
Member

jcfr commented Jul 8, 2020

Comments below are for information only and to capture some idea of future discussion

app->userSettings() and app->revisionUserSettings().

Moving forward, if we only support building Slicer with Slicer_STORE_SETTINGS_IN_HOME_DIR set to True, I am wondering if we should only have one function called app->settings().

At application startup, we would check if home folder is writable or not ... and have a relocatable application or not.

Function qSlicerCoreApplication::isRelocatableInstall() could even be added.

@lassoan
Copy link
Contributor Author

lassoan commented Jul 8, 2020

I am wondering if we should only have one function called app->settings()

It is still useful to allow sharing some user preferences (dark/light mode, DICOM database folder, dialog "don't show again" settings, etc.). With the proposed changes in this pull request, it is possible to choose between a fully isolated application (by having a Slicer.ini file in the Slicer Home folder) or sharing some user preferences between application instances (by not having Slicer.ini in Slicer Home).

@jamesobutler
Copy link
Contributor

Ok just wanted to throw out some commercial usage of Slicer as this is pretty different from the motivation of making it easy to share the installation with data for sharing in various cases. We still prefer the installation of Slicer/SlicerCustomApp in a system location not a user location. This is because customers have many users/researchers and they setup a local instance and use multiple Windows user accounts for access on the local machine and not a server environment. Installation is restricted to certain machines. Installing of different versions and installation of extensions not included is also restricted.

@lassoan
Copy link
Contributor Author

lassoan commented Jul 8, 2020

You can still keep the current behavior in your custom application by setting Slicer_STORE_SETTINGS_IN_APPLICATION_HOME_DIR to OFF in your custom application.

However, Slicer_STORE_SETTINGS_IN_APPLICATION_HOME_DIR actually makes shared installations safer and more predictable, too, as you can preinstall extensions for all users and you can prevent non-admin users from installing extensions.

Do you want your users to be able install extensions or Python packages?

@jamesobutler
Copy link
Contributor

You can still keep the current behavior in your custom application by setting Slicer_STORE_SETTINGS_IN_APPLICATION_HOME_DIR to OFF in your custom application.

Ok sounds good 👍

However, Slicer_STORE_SETTINGS_IN_APPLICATION_HOME_DIR actually makes shared installations safer and more predictable, too, as you can preinstall extensions for all users and you can prevent non-admin users from installing extensions.

This could be beneficial if we want to preinstall some extension that hasn't made it into our SlicerCustomApp yet. We wouldn't support users installing/removing extensions.

Do you want your users to be able install extensions or Python packages?

At this time, we do not want users to install extensions or python packages. We control the experience in a very specific manner.

@lassoan
Copy link
Contributor Author

lassoan commented Jul 8, 2020

I've moved the path replacement logic to CTK (ctkCoreSettings class) - @jcfr please have a look: commontk/CTK#922

Once this CTK prerequisite is merged, I'll finalize this PR.

@jcfr
Copy link
Member

jcfr commented Jul 8, 2020

please have a look: commontk/CTK#922

Thanks for working on this 🙏 , I just posted few comments.

@lassoan lassoan force-pushed the fully-portable-application branch 2 times, most recently from a73ed57 to c357267 Compare July 9, 2020 03:51
settings.setValue("Modules/AdditionalPaths",
ctkCoreSettings settings(q->extensionsSettingsFilePath(), QSettings::IniFormat);
settings.setApplicationHomeDirectory(qSlicerCoreApplication::application()->slicerHome());
settings.setApplicationHomePlaceholder("<APPLAUNCHER_DIR>");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using <APPLAUNCHER_SETTINGS_DIR> , also the ApplicationHomeDirectory may have to be set to match the location of the settings file instead.

Copy link
Member

@jcfr jcfr Jul 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To ensure PythonSlicer works, could you update to use APPLAUNCHER_SETTINGS_DIR ?

@lassoan
Copy link
Contributor Author

lassoan commented Jul 13, 2020

I've tested this on Windows and seems to work well.

@jcfr could you test in Linux?
@pieper could you test it on Mac?

Installing a larger extension package that contains loadable and CLI modules (such as SlicerRT or SlicerIGT) and testing if the modules run correctly should be enough. Slicer-(revision).ini and all installed extensions should show up in the (applicationHome)/NA-MIC folder.

Maybe you could also test if you put a Slicer.ini file into (applicationHome)/NA-MIC folder then it overrides the global ~/Slicer.ini file.

@pieper
Copy link
Member

pieper commented Jul 13, 2020

To test did you build this branch and create packages of it and also the extensions?

@lassoan
Copy link
Contributor Author

lassoan commented Jul 13, 2020

I've built and packaged extensions myself, but probably extension packages that you download from the dashboard should work, too.

@jcfr
Copy link
Member

jcfr commented Jul 13, 2020

@jcfr could you test in Linux?

A package is being built on metroxplex

@jcfr
Copy link
Member

jcfr commented Jul 14, 2020

Build failed with the following error:

/CMakeFiles/qSlicerBaseQTCore.dir/qSlicerCoreApplication.cxx.o -c /work/Slicer/Base/QTCore/qSlicerCoreApplication.cxx
In file included from /work/Slicer/Base/QTCore/qSlicerCoreApplication.cxx:63:0:
/work/Slicer/Base/QTCore/qSlicerCoreApplication_p.h:41:29: fatal error: ctkCoreSettings.h: No such file or directory
compilation terminated.
[2280/5319] Building CXX object Base/QTCore/CMakeFiles/qSlicerBaseQTCore.dir/qSlicerCoreCommandOptions.cxx.o
ninja: build stopped: subcommand failed.

@lassoan
Copy link
Contributor Author

lassoan commented Jul 14, 2020

/work/Slicer/Base/QTCore/qSlicerCoreApplication_p.h:41:29: fatal error: ctkCoreSettings.h: No such file or directory
compilation terminated.

ctkCoreSettings.h is the new class in CTK. Probably you just need to do a top-level build to get CTK updated.

@jcfr
Copy link
Member

jcfr commented Jul 14, 2020

Should the topic be rebased or CTK updated ?

It currently checkout this version:

ExternalProject_SetIfNotDefined(
Slicer_${proj}_GIT_TAG
"3ae43ea441fd759c30ed234dfa9b3c419ae1de8f"
QUIET
)

Whereas this one should be used:

commontk/CTK@f0bb5d7

@lassoan
Copy link
Contributor Author

lassoan commented Jul 14, 2020

You are right, the branch has to be rebased on latest Slicer master branch to get the new CTK hash.

@lassoan lassoan force-pushed the fully-portable-application branch from c357267 to 5fb7f2d Compare July 14, 2020 20:57
@lassoan
Copy link
Contributor Author

lassoan commented Jul 14, 2020

I've rebased the branch now. It should work now.

@jcfr
Copy link
Member

jcfr commented Jul 14, 2020

Build in progress, will report back once complete.

@jcfr
Copy link
Member

jcfr commented Jul 16, 2020

Build in progress, will report back once complete.

Linux build was successful 👍

Testing

Prerequisites:

  • Delete ~/.config/NA-MIC directories

Summary:

  • ✔️ Slicer starts
  • ✔️ Installing extension package (e.g SlicerRT)
  • ❎ Slilcer starts with installed extension

Analysis

After installing SlicerRT, Slicer fails to load the extension (see Error message section below)

Specifying --launcher-verbose option, we can see that the UserAdditionalSettings is not found:

./Slicer --launcher-verbose 
[...]
info: OrganizationDomain [www.na-mic.org]
info: OrganizationName [NA-MIC]
info: ApplicationName [Slicer]
info: ApplicationRevision [29218]
info: SettingsFileName [/home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/bin/SlicerLauncherSettings.ini]
info: SettingsDir [/home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/bin]
info: UserAdditionalSettingsDir [/home/jcfr/.config/NA-MIC]
info: UserAdditionalSettingsFileName []
info: UserAdditionalSettingsFileBaseName []
[...]
info: <APPLAUNCHER_DIR> -> [/home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64]
info: <APPLAUNCHER_NAME> -> [Slicer]
info: <APPLAUNCHER_SETTINGS_DIR> (RegularSettings) -> [/home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/bin]
info: <APPLAUNCHER_SETTINGS_DIR> (UserAdditionalSettings) -> [<APPLAUNCHER_SETTINGS_DIR-NOTFOUND>]
info: <APPLAUNCHER_SETTINGS_DIR> (AdditionalSettings) -> [<APPLAUNCHER_SETTINGS_DIR-NOTFOUND>]
[...]

For comparison, after installing SlicerRT from a preview build without this PR, the following is reported:

./Slicer --launcher-verbose
[...]
info: OrganizationDomain [www.na-mic.org]
info: OrganizationName [NA-MIC]
info: ApplicationName [Slicer]
info: ApplicationRevision [29154]
info: SettingsFileName [/home/jcfr/Downloads/Slicer-4.11.0-2020-06-17-linux-amd64/bin/SlicerLauncherSettings.ini]
info: SettingsDir [/home/jcfr/Downloads/Slicer-4.11.0-2020-06-17-linux-amd64/bin]
info: UserAdditionalSettingsDir [/home/jcfr/.config/NA-MIC]
info: UserAdditionalSettingsFileName []
info: UserAdditionalSettingsFileBaseName []
[...]
info: <APPLAUNCHER_SETTINGS_DIR> (RegularSettings) -> [/home/jcfr/Downloads/Slicer-4.11.0-2020-06-17-linux-amd64/bin]
info: <APPLAUNCHER_SETTINGS_DIR> (UserAdditionalSettings) -> [<APPLAUNCHER_SETTINGS_DIR-NOTFOUND>]
info: <APPLAUNCHER_SETTINGS_DIR> (AdditionalSettings) -> [<APPLAUNCHER_SETTINGS_DIR-NOTFOUND>]

Finally, starting Slicer specifying ./Slicer --launcher-additional-settings ./NA-MIC/Slicer-29218.ini ensure the SlicerRT extension can be loaded.

Proposed solutions

To address this, the application launcher should be updated so that the path to the user additional settings is read from the main settings instead of being inferred from the QSettings.

See https://github.com/commontk/AppLauncher/blob/b1d6dad48b11c2770a72ad879812995213ccfc6e/Base/ctkAppLauncherSettings.cpp#L60-L64

Optionally, function ctkAppLauncherSettingsPrivate::userAdditionalSettingsDir() could return a path configured into the main launcher settings file using the key userAdditionalSettingsDir

Error message

./Slicer 
  Error(s):
    Cannot load library /home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/NA-MIC/Extensions-29218/SlicerRT/lib/Slicer-4.11/qt-loadable-modules/libqSlicerBeamsModule.so: (libqSlicerBeamsModuleWidgets.so: cannot open shared object file: No such file or directory)
  Error(s):
    Cannot load library /home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/NA-MIC/Extensions-29218/SlicerRT/lib/Slicer-4.11/qt-loadable-modules/libqSlicerDicomRtImportExportModule.so: (libvtkSlicerDicomRtImportExportModuleLogic.so: cannot open shared object file: No such file or directory)
  Error(s):
    Cannot load library /home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/NA-MIC/Extensions-29218/SlicerRT/lib/Slicer-4.11/qt-loadable-modules/libqSlicerDicomSroImportExportModule.so: (libvtkSlicerDicomSroImportExportModuleLogic.so: cannot open shared object file: No such file or directory)
  Error(s):
    Cannot load library /home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/NA-MIC/Extensions-29218/SlicerRT/lib/Slicer-4.11/qt-loadable-modules/libqSlicerDoseAccumulationModule.so: (libvtkSlicerDoseAccumulationModuleLogic.so: cannot open shared object file: No such file or directory)
  Error(s):
    Cannot load library /home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/NA-MIC/Extensions-29218/SlicerRT/lib/Slicer-4.11/qt-loadable-modules/libqSlicerDoseComparisonModule.so: (libqSlicerDoseComparisonSubjectHierarchyPlugins.so: cannot open shared object file: No such file or directory)
  Error(s):
    Cannot load library /home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/NA-MIC/Extensions-29218/SlicerRT/lib/Slicer-4.11/qt-loadable-modules/libqSlicerDoseVolumeHistogramModule.so: (libqSlicerDoseVolumeHistogramSubjectHierarchyPlugins.so: cannot open shared object file: No such file or directory)
  Error(s):
    Cannot load library /home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/NA-MIC/Extensions-29218/SlicerRT/lib/Slicer-4.11/qt-loadable-modules/libqSlicerDosxyzNrc3dDoseFileReaderModule.so: (libvtkSlicerRtCommon.so: cannot open shared object file: No such file or directory)
  Error(s):
    Cannot load library /home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/NA-MIC/Extensions-29218/SlicerRT/lib/Slicer-4.11/qt-loadable-modules/libqSlicerExternalBeamPlanningModule.so: (libqSlicerBeamsSubjectHierarchyPlugins.so: cannot open shared object file: No such file or directory)
  Error(s):
    Cannot load library /home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/NA-MIC/Extensions-29218/SlicerRT/lib/Slicer-4.11/qt-loadable-modules/libqSlicerIsodoseModule.so: (libvtkSlicerIsodoseModuleWidgets.so: cannot open shared object file: No such file or directory)
  Error(s):
    Cannot load library /home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/NA-MIC/Extensions-29218/SlicerRT/lib/Slicer-4.11/qt-loadable-modules/libqSlicerPlanarImageModule.so: (libvtkSlicerPlanarImageModuleLogic.so: cannot open shared object file: No such file or directory)
  Error(s):
    Cannot load library /home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/NA-MIC/Extensions-29218/SlicerRT/lib/Slicer-4.11/qt-loadable-modules/libqSlicerPlastimatchPyModule.so: (libvtkSlicerPlastimatchPyModuleLogic.so: cannot open shared object file: No such file or directory)
  Error(s):
    Cannot load library /home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/NA-MIC/Extensions-29218/SlicerRT/lib/Slicer-4.11/qt-loadable-modules/libqSlicerPlmProtonDoseEngineModule.so: (libvtkSlicerPlmProtonDoseEngineModuleLogic.so: cannot open shared object file: No such file or directory)
  Error(s):
    Cannot load library /home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/NA-MIC/Extensions-29218/SlicerRT/lib/Slicer-4.11/qt-loadable-modules/libqSlicerRoomsEyeViewModule.so: (libvtkSlicerRoomsEyeViewModuleLogic.so: cannot open shared object file: No such file or directory)
  Error(s):
    Cannot load library /home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/NA-MIC/Extensions-29218/SlicerRT/lib/Slicer-4.11/qt-loadable-modules/libqSlicerSegmentComparisonModule.so: (libvtkSlicerSegmentComparisonModuleLogic.so: cannot open shared object file: No such file or directory)
  Error(s):
    Cannot load library /home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/NA-MIC/Extensions-29218/SlicerRT/lib/Slicer-4.11/qt-loadable-modules/libqSlicerSegmentMorphologyModule.so: (libvtkSlicerSegmentMorphologyModuleLogic.so: cannot open shared object file: No such file or directory)
  Error(s):
    Cannot load library /home/jcfr/Downloads/Slicer-4.11.0-2020-07-14-linux-amd64/NA-MIC/Extensions-29218/SlicerRT/lib/Slicer-4.11/qt-loadable-modules/libqSlicerVffFileReaderModule.so: (libvtkSlicerVffFileReaderLogic.so: cannot open shared object file: No such file or directory)
When loading module  "BatchStructureSetConversion" , the dependency "DicomRtImportExport" failed to be loaded.
libvtkSlicerRoomsEyeViewModuleLogicPythonD.so: cannot open shared object file: No such file or directory
libvtkSlicerExternalBeamPlanningModuleLogicPythonD.so: cannot open shared object file: No such file or directory
libvtkSlicerDicomRtImportExportModuleLogicPythonD.so: cannot open shared object file: No such file or directory
libvtkSlicerDoseAccumulationModuleLogicPythonD.so: cannot open shared object file: No such file or directory
libvtkSlicerBeamsModuleLogicPythonD.so: cannot open shared object file: No such file or directory
libvtkSlicerDoseVolumeHistogramModuleLogicPythonD.so: cannot open shared object file: No such file or directory
libvtkSlicerSegmentComparisonModuleLogicPythonD.so: cannot open shared object file: No such file or directory
libvtkSlicerPlastimatchPyModuleLogicPythonD.so: cannot open shared object file: No such file or directory
libvtkSlicerPlanarImageModuleLogicPythonD.so: cannot open shared object file: No such file or directory
libvtkSlicerDoseComparisonModuleLogicPythonD.so: cannot open shared object file: No such file or directory
libvtkSlicerDicomSroImportExportModuleLogicPythonD.so: cannot open shared object file: No such file or directory
libvtkSlicerIsodoseModuleLogicPythonD.so: cannot open shared object file: No such file or directory
libvtkSlicerPlmProtonDoseEngineModuleLogicPythonD.so: cannot open shared object file: No such file or directory
libvtkSlicerSegmentMorphologyModuleLogicPythonD.so: cannot open shared object file: No such file or directory
libvtkSlicerBeamsModuleMRMLPythonD.so: cannot open shared object file: No such file or directory
libvtkSlicerDoseVolumeHistogramModuleMRMLPythonD.so: cannot open shared object file: No such file or directory
libqSlicerDoseVolumeHistogramSubjectHierarchyPlugins.so: cannot open shared object file: No such file or directory
libqSlicerDoseComparisonSubjectHierarchyPlugins.so: cannot open shared object file: No such file or directory
libqSlicerDicomRtImportExportSubjectHierarchyPlugins.so: cannot open shared object file: No such file or directory
libqSlicerBeamsModuleWidgets.so: cannot open shared object file: No such file or directory
libqSlicerPlmProtonDoseEngineDoseEngines.so: cannot open shared object file: No such file or directory
libqSlicerBeamsSubjectHierarchyPlugins.so: cannot open shared object file: No such file or directory
libqSlicerExternalBeamPlanningModuleWidgets.so: cannot open shared object file: No such file or directory
libqSlicerIsodoseSubjectHierarchyPlugins.so: cannot open shared object file: No such file or directory
When loading module  "DvhComparison" , the dependency "DoseVolumeHistogram" failed to be loaded.
When loading module  "IGRTWorkflow_SelfTest" , the dependency "DicomRtImportExport" failed to be loaded.

@lassoan
Copy link
Contributor Author

lassoan commented Jul 16, 2020

Thanks a lot for the thorough testing and analysis!

We would still want to allow sharing a single Slicer.ini between all Slicer installs (which don't have a local Slicer.ini). So, the Slicer-NNN path cannot be stored in Slicer.ini.

Slicer looks for Slicer.ini in <applauncher_dir>/organization and if it is not found there then it looks for it in userprofile/organization folder. Currently, Slicer-NNN.ini is only looked for in <applauncher_dir>/organization or userprofile/organization folder, depending on CMake option chosen during Slicer build.

Launcher currently looks for these files only in userprofile/organization.

We should resolve these inconsistencies.

I think the best behavior would be to look for both ini files in <applauncher_dir>/organization folder first and if it is not found there then in userprofile/organization folder. What do you think?

@jcfr
Copy link
Member

jcfr commented Jul 16, 2020

We would still want to allow sharing a single Slicer.ini between all Slicer installs (which don't have a local Slicer.ini). So, the Slicer-NNN path cannot be stored in Slicer.ini.

Agreed.

[...] What do you think?

To clarify, the idea is to set userAdditionalSettingsDir in SlicerLauncerSettings.ini only if Slicer_STORE_SETTINGS_IN_HOME_DIR is TRUE

That way, the launcher will lookup the Slicer-NNN.ini in the expected location and this will nicely address the problem.

@jcfr
Copy link
Member

jcfr commented Jan 5, 2021

@lassoan I am currently building and testing this PR, did you squash and rebase or did you push any other changes ?

Once I am done, I will also integrate the relevant PR in CTK and publish an updated set of launcher binaries.

@lassoan
Copy link
Contributor Author

lassoan commented Jan 5, 2021

I pushed a change in qSlicerExtensionsManagerModelTest (fixes this test, no change in actual application code).

Slicer install tree was portable but not fully self-contained, as settings
and extensions were stored in the user profile folder.

Added a new option Slicer_STORE_SETTINGS_IN_APPLICATION_HOME_DIR (enabled
by default) that makes Slicer store all settings and extensions in the
application home folder, within (organizationName) subfolder (as QSettings
default constructor saves settings file into (organizationName) subfolder
by default), making the application fully portable.

To still allow having settings that are common to all installed application
versions for a user (e.g., DICOM database folder, confirmation popup
suppressions, ...), the common non-revision-specific is still stored
in the user profile directory by default. However, if the user places
an (applicationName).ini file in the application home folder/(organization)
then that file is used instead.

By default, (applicationName)=Slicer and (organization)=NA-MIC (or the
organization URL, depending on the operating system), and therefore
the .ini files are: (applicationHome)/NA-MIC/Slicer.ini
and (applicationHome)/NA-MIC/Slicer-(revision).ini; and extensions are
stored in (applicationHome)/NA-MIC/Extensions-(revision).

Also updated all path reading/writing in settings files so that paths
that are child folders of (applicationHome) are stored as relative paths.
All relative paths remain valid when the application is moved to a
different location.

Fixes Slicer#4966
Fixes Slicer#4722

----
List of CTKAPPLauncher changes:

$ git shortlog v0.1.27..v0.1.28 --no-merges
Andras Lasso (1):
      ENH: Allow user settings file to be in <APPLAUNCHER_DIR> (PR-114)

James Butler (1):
      COMP: Fix deprecation warning C4996

Jean-Christophe Fillion-Robin (3):
      Begin post-v0.1.27 development [ci skip]
      Fix deployment updating API key used for release upload
      CTKAppLauncher v0.1.28

Steve Pieper (2):
      BUG: fix crash calculating environment keys
      STYLE: use clearer syntax for constructor

----
List of CTKAppLauncherLib changes:

$ git shortlog 692164..a3100a4 --no-merges
Andras Lasso (1):
      ENH: Allow user settings file to be in <APPLAUNCHER_DIR> (PR-114)

Jean-Christophe Fillion-Robin (2):
      Fix deployment updating API key used for release upload
      CTKAppLauncher v0.1.28
@jcfr jcfr force-pushed the fully-portable-application branch from 9d1f2c1 to eaa45ad Compare January 5, 2021 20:46
@jcfr jcfr marked this pull request as ready for review January 5, 2021 20:51
@jcfr jcfr merged commit 008f8b9 into Slicer:master Jan 5, 2021
@lassoan lassoan deleted the fully-portable-application branch April 8, 2021 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

5 participants