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

BUG: Fix incorrect enter/exit called after scripted module reload #7351

Merged
merged 1 commit into from Nov 25, 2023

Conversation

Sunderlandkyl
Copy link
Member

When a scripted loadable module was reloaded using slicer.util.reloadScriptedModule, pythonSource was not changed in qSlicerScriptedLoadableModuleWidget. As a result, the previous enter/exit methods continued to be used by the scripted module.

Fixed by making setPythonSource accessible in Python, and using it to instantiate the new widget object.

Fix #7211

@Sunderlandkyl Sunderlandkyl force-pushed the python_reload_enter_exit_setup_fix branch from 7f4fe30 to 7f922e4 Compare November 6, 2023 20:58
pieper
pieper previously approved these changes Nov 6, 2023
Copy link
Member

@pieper pieper left a comment

Choose a reason for hiding this comment

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

My comment is just an idea. If you need this feature I'm okay with merging as-is.

Base/Python/slicer/util.py Outdated Show resolved Hide resolved
@@ -1372,10 +1372,12 @@ def reloadScriptedModule(moduleName):
for item in items:
parent.layout().removeItem(item)

# create new widget inside existing parent
widget = eval("reloaded_module.%s(parent)" % widgetName)
Copy link
Member

Choose a reason for hiding this comment

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

If we remove the use of the reloaded_module ivar, we should also revisit

with open(filePath, encoding="utf8") as fp:
reloaded_module = imp.load_module(
moduleName, fp, filePath, (".py", "r", imp.PY_SOURCE))
# find and hide the existing widget
parent = eval("slicer.modules.%s.widgetRepresentation()" % moduleName.lower())
for child in parent.children():
try:
child.hide()
except AttributeError:
pass

Copy link
Member

Choose a reason for hiding this comment

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

I agree with @jcfr - this code never seemed like a clean implementation but as I recall it was the best I could figure out at the time. If there's a cleaner and more maintainable implementation it would be great.

Copy link
Member Author

Choose a reason for hiding this comment

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

Currently the imp.load_module call still seems to be required. Without it, the module is not actually reloaded.

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.

Before moving forward we should revisit code setting the reloaded_module internal variable as this is likely not needed anymore.

@jcfr
Copy link
Member

jcfr commented Nov 7, 2023

The root cause of this issue is that each C++ instance of a scripted module maintains a reference to a qSlicerPythonCppAPI instance through the d->PythonCppAPI internal variable. When virtual functions are invoked from Python (for instance, when the module manager calls qSlicerAbstractModuleWidget::enter()), the previous reference handles are still utilized.

To comprehensively resolve this, we should implement a systematic solution by introducing a "reload()" function at the C++ level. This function will ensure that the underlying PythonCppAPI object is updated correctly. To prevent any potential "catch 22" scenarios, the reload API should be accessible via the module manager.

I will propose a fix for this issue.

@lassoan
Copy link
Contributor

lassoan commented Nov 14, 2023

@Sunderlandkyl this looks good to me overall. Could you rebase on latest main and resolve the conflicts? Thank you!

@Sunderlandkyl
Copy link
Member Author

@jcfr

The root cause of this issue is that each C++ instance of a scripted module maintains a reference to a qSlicerPythonCppAPI instance through the d->PythonCppAPI internal variable. When virtual functions are invoked from Python (for instance, when the module manager calls qSlicerAbstractModuleWidget::enter()), the previous reference handles are still utilized.

To comprehensively resolve this, we should implement a systematic solution by introducing a "reload()" function at the C++ level. This function will ensure that the underlying PythonCppAPI object is updated correctly. To prevent any potential "catch 22" scenarios, the reload API should be accessible via the module manager.

I will propose a fix for this issue.

Should I keep this PR open. or close it in favor of a more comprehensive solution?

@jcfr jcfr force-pushed the python_reload_enter_exit_setup_fix branch from d7b3534 to 6e976e7 Compare November 14, 2023 18:22
@jcfr
Copy link
Member

jcfr commented Nov 14, 2023

Should I keep this PR open. or close it in favor of a more comprehensive solution?

I will follow-up this afternoon by either:

  • creating a pull-request proposing an "cleaner" approach
  • or slightly cleaning up this pull-request and creating an issue documenting the "proper" workaround

In the meantime, I just rebased and force-pushed the topic against the current main

@jcfr jcfr added this to the Slicer 5.5 milestone Nov 17, 2023
@jcfr jcfr self-assigned this Nov 21, 2023
@jcfr jcfr force-pushed the python_reload_enter_exit_setup_fix branch from 6e976e7 to c8e07fe Compare November 25, 2023 08:32
When a scripted loadable module was reloaded using slicer.util.reloadScriptedModule,
pythonSource was not changed in qSlicerScriptedLoadableModuleWidget.
As a result, the previous enter/exit methods continued to be used by the
scripted module.

Fixed by adding "reload()" function accessible from Python

Fix Slicer#7211

Co-authored-by: Jean-Christophe Fillion-Robin <jchris.fillionr@kitware.com>
@jcfr jcfr force-pushed the python_reload_enter_exit_setup_fix branch from c8e07fe to 34f6ed0 Compare November 25, 2023 08:38
@jcfr jcfr merged commit 8999568 into Slicer:main Nov 25, 2023
4 of 5 checks passed
@Sunderlandkyl Sunderlandkyl deleted the python_reload_enter_exit_setup_fix branch January 15, 2024 17:22
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.

Reloading a scripted module causes unexpected behavior when switching modules
5 participants