Skip to content

feat[canvas]: Patch the orange-canvas to add an export and import menus as ewoks json workflow - #503

Merged
maxenceprog merged 2 commits into
mainfrom
issue_79
Oct 16, 2026
Merged

maxenceprog merged 2 commits into
mainfrom
issue_79

Conversation

@maxenceprog

@maxenceprog maxenceprog commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

closes #79

PR summary

Done with Claude... I made the review and looks ok but maybe i miss something. It patches orangecanvas.application.canvasmaiCanvasMainWindow.__init__. This is maybe a beit creepy but not sure there is a more conventional solution as the main canvas init is done in main managed by oasys or orange.

This add two menus Export Ewoks workflow... and Import Ewoks workflow.... This uses ows_to_ewoks api to convert ows file as a json workflow for Export and the reverse principle for Import.

Screencast.from.2026-10-14.17-25-40.mp4

AI Disclosure

  • No AI used
  • AI tool(s) claude used for generate the code

@codecov

codecov Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 56.52174% with 30 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/ewoksorange/gui/canvas/menu.py 56.71% 29 Missing ⚠️
src/ewoksorange/gui/canvas/main.py 50.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@maxenceprog
maxenceprog force-pushed the issue_79 branch 2 times, most recently from 2b3d227 to a803fef Compare October 14, 2026 15:26
Comment thread src/ewoksorange/gui/canvas/main.py Outdated


def main(argv=None):
patch_canvas_menu()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Maibe there is a better solution but as the main function is managed by orangecanvas, not sure there is a better way

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

For Orange, main calls Main().run(argv)

You could override Main in this case to tune the canvas main window.

However, it won't work with oasys

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

i think i look at the code of oasys and OASYSMainWindow inhirits from orangecanvas.application.canvasmai.CanvasMainWindow.

https://github.com/oasys-kit/OASYS2/blob/main/oasys2/canvas/application/canvasmain.py

But yes maybe cleaner anyway to override main i will look at that

@maxenceprog maxenceprog Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Oh ok I misunderstand your comment. You said the method you propose will not work with Oasys right?

So I would say we have to keep the patch like now ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You said the method you propose will not work with Oasys right?

Yes, I fear so.

So I would say we have to keep the patch like now ?

Yes, I don't see another way.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Rather than patch_canvas_menu, the function name could be clearer.

Like add_ewoks_import_export_actions

if _patched:
return
_patched = True
original_init = CanvasMainWindow.__init__

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have verified in the code and Oasys / Orange looks to both use orangecanvas.application.canvasmaiCanvasMainWindow so it should works

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Put this as comment perhaps?

@maxenceprog maxenceprog self-assigned this Oct 14, 2026
@maxenceprog
maxenceprog requested a review from a team October 14, 2026 15:32
@maxenceprog
maxenceprog marked this pull request as draft October 14, 2026 15:41
@maxenceprog

Copy link
Copy Markdown
Contributor Author

TODO add unit tests and changelog

Comment thread src/ewoksorange/gui/canvas/main.py Outdated

def __init__(self, *args, **kwargs):
original_init(self, *args, **kwargs)
_add_actions(self)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Adding actions is a good idea. Perhaps there are other ways to do this without patching? Not sure.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes. One question is: how much different is it from the already existing Save / Save As / Open function that already exists ?
At the end this is only the file format that changes right ?

@maxenceprog maxenceprog Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes we save as a json ewoks workflow

equivalent to an ewoks convert .ows <-> .json

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sorry, I wasn’t clear. My question is whether we should add new actions or, if possible, reuse the existing one and adapt the current code (QDialog, save/load functions).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I can investigate first if this is possible (Without going to deep into monkey patching / changing orange canvas api)

But in my opinion. It looks normal for me to have an "export import" pattern. --> The native way to save load orange workflow is ows.

For me this is the same as word. You save / load as docx but you can export import as open office format

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I agree with you on the technical side. That said, I keep wondering: what is the user actually expecting here? Could it be confusing for them to have to choose between saving and exporting? And are we at risk of ending up with too many actions?

I’m not pushing for one option over the other, but I think we should at least ask ourselves these questions first. The technical side can come second.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we were to ignore completely the technical side, yes I agree that it would be better to have "Ewoks workflow" as a new filter in the open/saving box:

image

I'll try to have Claude provide this as an alternative to compare but no promises.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

See #504

As expected, four things need patching: CanvasMainWindow._open_workflow_dialog, CanvasMainWindow.open_scheme_file, CanvasMainWindow.save_scheme_as and the QFileDialog for saving.

Also, the opening for OASYS needs to be patched separately since the implementation is slightly different.

What do you think @payno ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hard to say. Modifying _open_workflow_dialog and QFileDialog is playing with fire.

But save_scheme_as could be fully rewrite or we could ask the orange team to do some modifications.
_open_workflow_dialog is used at three places where there is public API:

        def open_scheme():
            dlg = self._open_workflow_dialog()
            dlg.setParent(dialog, Qt.Dialog)
            dlg.fileSelected.connect(self.open_scheme_file)
            dlg.accepted.connect(dialog.accept)
            dlg.exec()
        ...
    def open_scheme(self):
        # type: () -> None
        """
        Open a user selected workflow in a new window.
        """
        dlg = self._open_workflow_dialog()
        dlg.fileSelected.connect(self.open_scheme_file)
        dlg.exec()

    def open_and_freeze_scheme(self):
        # type: () -> None
        """
        Open a user selected workflow file in a new window and freeze
        signal propagation.
        """
        dlg = self._open_workflow_dialog()
        dlg.fileSelected.connect(partial(self.open_scheme_file, freeze=True))
        dlg.exec()

So I agree this is more code to maintain. But at a first glance it could be 'clean' enough.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's discuss this in the next workflow meeting to reach a collective agreement then.

In the mean time, I think we can merge this PR

@maxenceprog
maxenceprog force-pushed the issue_79 branch 2 times, most recently from 013ed22 to 9860deb Compare October 15, 2026 11:24
@maxenceprog
maxenceprog requested a review from loichuder October 15, 2026 11:26
@maxenceprog
maxenceprog marked this pull request as ready for review October 15, 2026 11:29
Comment thread src/ewoksorange/tests/test_canvas_io.py Outdated
# Here i import to the canvas then re export it and verify the exported json.

with OrangeCanvasHandler() as handler:
monkeypatch.setattr(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Not a fan of monkeypatch but there seem to be no other way

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yep me too but that's the better way to bypass the dialog

Comment thread src/ewoksorange/gui/canvas/main.py Outdated


def main(argv=None):
patch_canvas_menu()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Rather than patch_canvas_menu, the function name could be clearer.

Like add_ewoks_import_export_actions

if _patched:
return
_patched = True
original_init = CanvasMainWindow.__init__

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Put this as comment perhaps?

Comment thread src/ewoksorange/tests/test_canvas_io.py Outdated
Comment thread src/ewoksorange/gui/canvas/menu.py Outdated
Comment thread src/ewoksorange/gui/canvas/menu.py Outdated
Comment thread src/ewoksorange/gui/canvas/menu.py Outdated
Comment thread src/ewoksorange/gui/canvas/menu.py Outdated
import_action = QAction(
"Import Ewoks workflow...", window, objectName="action-import-ewoks"
)
import_action.triggered.connect(lambda: _import_ewoks(window))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Not sure if this raises issues from the Qt lifecycle.

@t20100 ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's likely to keep some reference to keep access to window and thus may create a reference cycle.

Basically except for module-level functions and methods, connecting anything else (lambda, closure function, partials) may create a reference cycle that may delay destruction some widgets.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes that's maybe bad as if user click on new window and close the first one, so the RAM will not be freed right now.

For software like darfix this is an issue

So I need to change this but the problem is as this is patching I cannot use class method... I have to think about this...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@maxenceprog Don't think too hard until we made a decision on whether we make new actions or patch the existing ones 😉

@loichuder loichuder left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As mentioned, let's merge this as a temporary solution

Comment thread src/ewoksorange/gui/canvas/menu.py Outdated
Co-authored-by: Loïc Huder <42204205+loichuder@users.noreply.github.com>
@maxenceprog
maxenceprog enabled auto-merge (rebase) October 16, 2026 08:45
@maxenceprog
maxenceprog merged commit d32fa9d into main Oct 16, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

needed saving the workflow in json format

5 participants