Giter Site home page Giter Site logo

Comments (14)

ggarra13 avatar ggarra13 commented on August 22, 2024 2

Check out the recently released mrv2 v0.6.3 where the plug-in system is in place!

from mrv2.

ggarra13 avatar ggarra13 commented on August 22, 2024 1

I fixed the printing of Python stdout / stderr to happen as the script runs.

from mrv2.

ggarra13 avatar ggarra13 commented on August 22, 2024

Currently you can run a python script from the command-line with:

mrv2 -ps <script.py> or mrv2 -pythonScript <script.py>

The script will run without opening a window and it will then exit mrv2. You currently cannot pass any parameters to this script (and I am not sure if it can be done).

You can also add custom scripts to the Python Panel. You open the Python Panel, go to Scripts in the Python Panel menu and you can then pick a script from the file system. The script will be added to the Scripts menu and will get saved in the mrv2.prefs file in your home directory. You can, currently, have up to 10 scripts stored this way. Your idea of using an environment variable is interesting and will require some more work.

Regarding PySide2, mrv2 is built with FLTK, while PySide2 is Qt based. As such, if you open a PySide2 window, you may need to start and control the Qt event loop yourself with (I believe) app.exec_. While the Qt event loop runs, the FLTK event loop (ie. mrv2's playback) will, I believe, remain frozen unless you call cmd.update() (Like it is shown in the timeline.py python demo).

from mrv2.

BigRoy avatar BigRoy commented on August 22, 2024

I noticed you could pass a python script, but of course it'd just close down directly after. The menu entries don't need to be Qt necessarily, but if there's a Python plug-in system in place which could add menu entries it'd be great. A simple example could be something like (psuedocode of course):

Setting environment variable MRV_PLUGIN_PATH for search paths for plugins.
Then MRV could search those paths and run the plugins it finds

# Discover all plugins
plugins = []
plugin_paths = os.environ.get("MRV_PLUGIN_PATH", "").split(os.pathsep)
for plugin_path in plugin_paths:
    discovered_plugins = discover_plugins(plugin_path)
    for plugin in discovered_plugins:
        plugins.append(plugin)

# Allow users to enable/disable plugins and store those settings
# so we read the preferences to see the preferred state
for plugin in plugins:
    if get_plugin_load_state(plugin.name, default=plugin.default_load_state):
        plugin.load()

Then any loaded plugin could also expose a customize_menu adding in custom menu entries (or even replacing existing ones?):

class Plugin(MrvPlugin):

    name = "MyPlugin"
    default_load_state = True

    def load(self):
        pass

    def unload(self):
        pass

    def customize_menu(self, menus):
        """Customize menus dict by adding your own entries.

        Any `dict` becomes a submenu.
        Any `list` or `tuple` is expected to be of length 2
        containing the label and the python callback on click.

       Arguments:
           menus (dict): The current menus that can be
               altered by the plugin.

       Returns:
           dict: The altered menus

        """
        # Allow to add in custom menu entries
        menus["MyMenu"] = [
            ("Load", self.on_load),
            ("Manage", self.on_manage)
            {"Submenu": [
                ("Submenu Action", self.on_submenu_action)
            ]
        ]
        return menus

Likely there's a much better API to define for this, but it's just a quick showcase of what could be done. I believe OpenRV also has a python plugin system which can define custom menu entries, etc.

By building a plugin system like this it might also become trivial to start writing custom annotation tooling through Python as an extension/plugin too.

from mrv2.

ggarra13 avatar ggarra13 commented on August 22, 2024

Ok. I have the basic plug-in system in place for you to test. It is in main HEAD (if you compile from source).

There's documentation for it in Help->Documentation and click on the Python / Plug-in System. There's one demo script in the python/plug-ins directory of mrv2. It cretes a Python menu, a Python/Playback/Forwards and Python/Playback/Backwards submenus.

Give it a go and I await your feedback.

from mrv2.

BigRoy avatar BigRoy commented on August 22, 2024

Sweet! It's great seeing a prototype. Mostly busy with other stuff on my end these past few days so I'm slow to reply, sorry about that.

Regarding the plugin system, I think we can improve by having MRV itself define the base Plugin class internally, e.g. mrv.plugins.Plugin so that developers can inherit from it.

class MyPlugin(mrv.plugins.Plugin):
    ...

The benefit to that is having autocomplete for existing functions and the base plugin class can also itself document its functionality a bit with docstrings/type hints, etc.

The plugin system would then go over the Python files, and find any classes that inherit from mrv.plugins.Plugin - those are plugins that can be loaded. This also means that technically a single Python file could hold multiple plugins and even custom systems could be build that load plugins dynamically from elsewhere importing it into the namespace of that file. Anyway, that's likely a very complex use case but it opens up ways to consistency.

That also means that e.g. the base class would already implement the active method returning True by default. Over time that particular method could even expand to get its active state from some stored preferences file. Say each Plugin requires an id attribute which defines its unique id. (Or we could just use name for that?)

class Plugin(mrv2.plugins.Plugin):
    id = "my_custom_plugin"

Then we could start building a system into MRV which could maybe toggle on/off Plugins based on user preferences where maybe the default active method does that:

def active(self):
    is_active = get_plugin_pref(self.id, "active", default=True)
    return is_active

Anyway. Let me try and fiddle with the system that's currently there.

from mrv2.

BigRoy avatar BigRoy commented on August 22, 2024

Did a quick test run. It does seem to work:

image

It'd be great however if we can define the order of the menu entries ourselves. Now it seems to be a set in the example plug-in (which is not sorted) and using a list also doesn't work. My example plug-in used for this screenshot is:

class Plugin:
    def on_load(self):
        print("Load..")
        
    def on_manage(self):
        print("Manage..")
        
    def on_library(self):
        print("Library..")
        
    def on_workfiles(self):
        print("Workfiles..")
   
    def menus(self):
        top = "OpenPype"
        menu = [ 
            (f"{top}/Load...", self.on_load),
            (f"{top}/Manage...", self.on_manage),
            (f"{top}/Library...", self.on_library),
            (f"{top}/Workfiles...", self.on_workfiles),
            (f"{top}/Submenu/Test...", self.on_workfiles),
            
        ]
        return menu

from mrv2.

ggarra13 avatar ggarra13 commented on August 22, 2024

It'd be great however if we can define the order of the menu entries ourselves. Now it seems to be a set in the example plug-in (which is not sorted) and using a list also doesn't work. My example plug-in used for this screenshot is:

This is also fixed in HEAD.

from mrv2.

ggarra13 avatar ggarra13 commented on August 22, 2024

The plugin system would then go over the Python files, and find any classes that inherit from mrv.plugins.Plugin - those are plugins that can be loaded. This also means that technically a single Python file could hold multiple plugins and even custom systems could be build that load plugins dynamically from elsewhere importing it into the namespace of that file. Anyway, that's likely a very complex use case but it opens up ways to consistency.

Sadly, I think I cannot do that. A Plugin is one per file and has to be named Plugin. I'll investigate more, though.

from mrv2.

ggarra13 avatar ggarra13 commented on August 22, 2024

Sadly, I think I cannot do that. A Plugin is one per file and has to be named Plugin. I'll investigate more, though.

Ok. I got it working as you wanted. The changes that you requested, the one line bug fix in the python panel, plus the updated docs and demo plug-in are in this beta of v0.6.4:

.exe:
https://mega.nz/file/KOpVWIgI#b1YnuLQC5NdS_DAzhxqhp1OdMJ-cFz_3YCfUSwy4TC0

.zip:
https://mega.nz/file/HGgEAT7R#BFQacTJbdf5r8EqqovCKbyC3zPAM7nlU57WRbqL59YM

from mrv2.

BigRoy avatar BigRoy commented on August 22, 2024

Thanks! Great improvement. Now using the latest docs with the build I came to this test plugin:

from mrv2 import plugin

class MyPlugin(plugin.Plugin):
    def on_load(self):
        print("Load..")
        
    def on_manage(self):
        print("Manage..")
        
    def on_library(self):
        print("Library..")
        
    def on_workfiles(self):
        print("Workfiles..")
   
    def menus(self):
        top = "Testing"
        menu = {
            f"{top}/Load...": self.on_load,
            f"{top}/Manage...": self.on_manage,
            f"{top}/Library...": self.on_library,
            f"{top}/Workfiles...": self.on_workfiles,
            f"{top}/Submenu/Test...": self.on_workfiles   
        }
        return menu

Order is now preserved 👍 and the above works fine.

What stood out to me however was that this didn't work:

import mrv2.plugin

class MyPlugin(mrv2.plugin.Plugin):
     ...

Erroring with:

ERROR: [python] AttributeError: module 'mrv2' has no attribute 'plugin'

Which seems confusing? Is there something special happening on the imports of the mrv2 python modules?
Anyway, from mrv2 import plugin works so I guess there's a workaround for now.

Very nice work!

from mrv2.

ggarra13 avatar ggarra13 commented on August 22, 2024
import mrv2.plugin

This, for some reason, I cannot make it work. Just import mrv2 and use the full name or do a from mrv2 import plugin and use plugin.Plugin.

class MyPlugin(mrv2.plugin.Plugin):

This now works.

from mrv2.

ggarra13 avatar ggarra13 commented on August 22, 2024

I thought I should mention that v0.7.9 has introduced pyFLTK support. This integrates more seamlessly with mrv2 than PySide2 as it can run within mrv2's event loop (ie. you can have the player playing videos while you interact with your own windows, buttons and requesters). See:

https://youtu.be/cFs1ktE_7z4

from mrv2.

BigRoy avatar BigRoy commented on August 22, 2024

Regarding PySide2, mrv2 is built with FLTK, while PySide2 is Qt based. As such, if you open a PySide2 window, you may need to start and control the Qt event loop yourself with (I believe) app.exec_. While the Qt event loop runs, the FLTK event loop (ie. mrv2's playback) will, I believe, remain frozen unless you call cmd.update() (Like it is shown in the timeline.py python demo).

I just wanted to mention that it seems like calling cmd.update() works fine - however it does not update any printed output into MRV2's Python panel top log area. The output doesn't appear until the Qt application loop actually closes down it seems.

from mrv2.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.