How to use runMenuCommand() in python script?



  • How to call nested commands? For example, if I want to call View - Show Symbol - Show Indent Guide, I tried
    notepad.runMenuCommand('View\Show Symbol','Show Indent Guide')
    and
    notepad.runMenuCommand('View','Show Indent Guide')
    , but nothing happens.

    Also, I want to invoke one python script through another one. I tried notepad.runPluginCommand('Python Script','Scripts\anotherScript') . Didn’t work.
    I also tried making anotherScript a menu item (through python script configuration) first, and then notepad.runPluginCommand('Python Script','anotherScript'). Also didn’t work.

    Thanks in advance.



  • @古旮

    for builtin menu function you do use menuCommand, for foreign menus like the famous TextFX you use runMenuCommand.

    To answer your first question notepad.menuCommand(MENUCOMMAND.VIEW_INDENT_GUIDE)

    Concerning your second question I would say it depends what exactly is needed to be done like providing arguments …
    What about using the python way and importing it?

    Eko



  • @古旮,

    It turns out you don’t need to prefix with the “View” menu first:
    notepad.runMenuCommand('Show Symbol','Show Indent Guide')

    From the PythonScript console, if you want to run a script, the syntax is notepad.runPluginCommand('Python Script', 'anotherScript') – whether that script is only in the Scripts submenu, or whether it’s also been made a menu-item through the Configuration. At least for me, on NPP 7.5.8 with PythonScript 1.3.0.0.

    However, when I tried to run one script from another, I got a dialog box: “Another script is currently running. Running two scripts at the same time could produce unpredicable [sic] results, and is therefore disabled.” So they obviously don’t want you doing it that way.

    I had partial success in using Python’s import syntax:

    console.show()
    console.write("Hello World")
    import SomeOtherScript
    # note that you drop the ".py" off of SomeOtherScript.py
    

    If I ran this, it would write to the PythonScript console, then attempt to run SomeOtherScript. However, the attempt would only be successful if SomeOtherScript.py included the from Npp import *: most of my scripts rely on that having been imported in startup.py, but apparently when you add the level of indirection through import, it loses that overhead. When I added from Npp import *, then it would work.

    As a second “however”: it would also only import/run the extra script once. If I tried again, it wouldn’t. My experiments and researched showed that it’s because the “module” (script) has already been “imported”, and since the PythonScript module has just one instance of Python, it stays “imported”. I have some ideas on how to get around that … but not enough time to get to that right now. I’ll come back to this later today.



  • @古旮,

    Okay, I found a couple more minutes right away while waiting for something else to get ready:

    If the primary script is below, and if anotherScript.py starts with from Npp import *, (and if primaryScript.py and anotherScript.py are in the same directory), then I believe the following will work: it will re-run anotherScript every time primaryScript is run.

    console.show()
    console.write("\nHello World\n")
    
    # run the script called "anotherScript.py":
    if 'anotherScript' in globals():
        console.write("anotherScript already exists\n")
        reload(anotherScript)
    else:
        console.write("anotherScript will be loaded\n")
        import anotherScript
    
    console.write("\nThe End\n")
    

    (There may be some other caveats, but this combination worked in my experiments.)

    In your final version, you don’t need all the console.write()s: those were just to show what was going on.



  • @PeterJones

    It turns out you don’t need to prefix with the “View” menu first:

    nice one - wasn’t aware of this, so menuCommand is not needed anymore??

    You are correct, python caches imported modules to avoid and prevent import loops.
    One way to force to reimport the module would be to use the reload function but a
    better way would be do define the function needed and call it explicitly like

    SomeOtherScript.py contains

    import time
    def someOtherFunction():
        print('someOtherFunction:{}'.format(time.time()))
    

    and the main script contains something like

    import SomeOtherScript
    print('main script')
    SomeOtherScript.someOtherFunction()
    

    Eko



  • I find that I can use the Python function execfile() to run one script from another…no need to worry about import/reload etc.



  • @Alan-Kilborn

    yes, you can use exec or execfile to execute python code but have to be aware
    that it differs how the code gets included.
    Let’s assume you do have a script with name SomeOtherScript.py and within there is
    one function SomeOtherFunction as well as one variable SomeOtherVariable

    If you import SomeOtherScript then you’ve ensured that SomeOtherFunction and SomeOtherVariable
    are only accessible via the module namespace.

    By using execfile or exec without using the optional global and local dicts you are importing
    SomeOtherFunction and SomeOtherVariable into the namespace of the current script which might
    override existing functions/variables.

    Eko



  • @Eko-palypse said:

    By using execfile or exec without using the optional global and local dicts you are importing
    SomeOtherFunction and SomeOtherVariable into the namespace of the current script which might
    override existing functions/variables.

    Yes but Pythonscripts are usually small things with very well compartmentalized naming. At least mine are. I use the technique that someone else here showed, where funcs/vars have names that start with abbreviations of the script file names. Thus no collisions; but yes, no guarantees.



  • @Alan-Kilborn

    absolutely agreed - there is no wrong or right here as long as there is no requirement which
    forces either the one or the other way to use.

    Eko



  • @Alan-Kilborn said:

    I use the technique that someone else here showed, where funcs/vars have names that start with abbreviations of the script file names

    BTW this is a good thing to do in general. Consider a different scenario, no longer the one where running one script from another. Say you run script 1. Ok, fine, script one is finished and done. Now you want to use script 2. Well, script 1’s variables and functions still exist, so you could potentially be accessing old data from script 2. Not with the artificial namespace forcing (the names starting with abbrevs)–doesn’t happen.



  • @Alan-Kilborn

    I assume as long as you are the only one who maintains and executes the scripts it is ok
    but as soon as further people are involved it might get a little bit tricky and, personally,
    I find it hard to read and is there really a benefit of, let’s say XYZ_myfunction contrary to XYZ.myfunction?
    Guess, is a matter of taste.

    Eko


Log in to reply