How do I change available menus in the mainMenu bar?

I am trying to implement a custom menu as part of moving Jupyter Physical Science Lab to Jupyter Lab from the old notebook, in preparation for additional updates. At this point I am just trying to wrap my mind around menus in Jupyter lab.

Two issues:

  1. Most important to me. I have managed to add a custom menu to the mainmenu bar using IMainMenu.addMenu(). As there does not seem to be a “disable” option, I want to remove the menu under some circumstances. I can see that IMainMenu is the interface for the mainMenu class, which derives from @lumino\widgets\MenuBar, which does have .removeMenu() and .insertMenu() methods. How do I access these as that seems the cleanest approach within the current JLab structure? I am sure I am overlooking something obvious. I think I am unclear about how modularized syntax in Typescript handles this inheritance.

  2. Poking around in other people’s extensions, it appears there may be a way to disable and enable whole menus using the settings registry system. However, I do not understand where the ISettingsRegistry.load(...) method that I see being used is actually hidden. I do not see it in the documentation. I can dig down to something called fetch, but how that would be used properly in code is unclear to me. Anyway, I need some direction to more detailed documentation or examples of how to use settings from a schema or programmatically.

Thanks.

I would be happy just adding the attribute hidden = 'true' to the <li> that contains the menu label in the Main menu bar. I could do this by accessing the DOM directly, but am not sure that is a good idea.

JupyterLab menu items can be disabled via JSON settings, documented forusers here and for extension developers here. I would suggest you go to Settings → Setttings Editor → JSON Settings Editor → Main Menu and edit the JSON accordingly. Here is JupyterLab without “File” menu:

You can also change individual items in menu. Once you are happy with the result you need to prepare an overrides.json file as documented here (not that it has one more nesting level as it can provide overrides for settings for multiple packages) and drop it in an appropriate location on the filesystem.

3 Likes

Thanks for the description. I had managed to edit things in the settings menu for already existing menus. My ultimate goal is to programmatically enable and disable my custom menu based on metadata in the currently focused notebook. I am first attempting the two following simpler operations, which I cannot get to work following any of the examples I have found, nor (so far) by digging through the code of JupyterLab:

  1. generating a custom menu using schemas. The extension example does not work. It only adds commands to the pallet. I think there is some missing settings registry boiler plate that I am not figuring out or a naming issue. I have tried the following code based on a couple of other extensions that seem to work. However, in my own extension pkg I am not getting it to work.
  • pkg/package.json contains the following info about schemas:
[snip]
 "jupyterlab": {
        "extension": true,
        "outputDir": "JPSLMenu2/labextension",
        "schemaDir": "schema"
    },
[snip]
  • pkg/schema/plugin.json:
{
  "title": "JPSLMenu2",
  "description": "Main Menu Example settings.",
  "jupyter.lab.menus": {
    "main": [
      {
        "id": "JPSLMenu2",
        "label": "JPSL Tools",
        "items": [
          {
            "command": "Item-1:JPSLMenu2:main-menu",
            "args":{"label": "Item 1", "origin": "from the menu"}
          },
          {
            "command": "help:open",
            "args": {"text": "Gutow Homesite", "url":"https://cms.gutow.uwosh.edu/Gutow", "newBrowserTab":"true"}
          }
        ],
        "rank": 80
      }
    ]
  },
  "additionalProperties": false,
  "type": "object"
}
  • pkg/src/index.ts:
import {
  JupyterFrontEnd,
  JupyterFrontEndPlugin,
} from '@jupyterlab/application';

import { IMainMenu } from '@jupyterlab/mainmenu';
import { ICommandPalette } from '@jupyterlab/apputils';
import { ISettingRegistry } from '@jupyterlab/settingregistry';

/**
 * Initialization data for a main menu extension.
 */
const extension: JupyterFrontEndPlugin<void> = {
  id: 'JPSL2Menu:plugin',
  autoStart: true,
  requires: [IMainMenu, ICommandPalette, ISettingRegistry],
  activate: (app: JupyterFrontEnd,
      MainMenu: IMainMenu,
      palette: ICommandPalette,
      settingRegistry: ISettingRegistry) => {
    const { commands } = app;

    // Add a command
    const command = 'Item-1:JPSLMenu2:main-menu';
    commands.addCommand(command, {
      label: 'Item 1',
      caption: 'Item 1',
      execute: (args: any) => {
        console.log(
          `Item 1 has been called ${args['origin']}.`
        );
        window.alert(
          `Item 1 has been called ${args['origin']}.`
        );
      },
    });

    // Add the command to the command palette
    const category = 'JPSL Tools';
    palette.addItem({
      command,
      category,
      args: { origin: 'from the palette' },
    });
    // Add a menu using the settings settingRegistry
    settingRegistry.load(extension.id);
  },
};

export default extension;

I get the following error from jupyter:

  tornado.web.HTTPError: HTTP 404: Not Found (Schema not found: <my home directory>/.local/share/hatch/env/virtual/jpslmenu2/a-jj0_aW/jpslmenu2/share/jupyter/lab/schemas/JPSL2Menu/plugin.json)

This leads me to believe that something is wrong about the name I am giving the schema file or how it is referred to in the code. However, all the combinations I have tried do not work. The actual location of the schema for the built project is:

<my home directory>/.local/share/hatch/env/virtual/jpslmenu2/a-jj0_aW/jpslmenu2/share/jupyter/labextensions/JPSL2Menu/schemas/JPSL2Menu/plugin.json)

Which is accessed via a symlink in <my home directory>/.local/share/hatch/env/virtual/jpslmenu2/a-jj0_aW/jpslmenu2/share/jupyter/labextensions/ to the built project in my project directory. I did not set this up. It is what happens when I use jlpm build in the project that was set up using the Copier template for extensions.

Looking at settings_utils.py, I am wondering if I need to set the path to labextensions somehow and if I need an extra colon in plugin.id?

  1. taking a menu I have generated programmatically (which does work) I can hide the menu by directly accessing the DOM, but think it would be better to do it through JupyterLab. Through JupyterLab I can make the menu not show any items using menu.hide() and activate it again with menu.show(). These only impact the menu popup, which appears to be a reuse of ContextMenu, but leaves the menu visible in the menu bar. Is there a way to use the settings registry with programmatically defined menus?

For either of these am I making silly errors, or can you at least point me in the right direction?

Thanks.

1 Like

Do you have schema folder files in “files” stanza?

Thank you. That was one error, but does not fix the symptoms. I still get the same file not found path issue described in my previous post.

It is a bit of a guessing game without knowing your full source code and directory structure. I would suggest using the copier template again in a fresh directory and with a fresh virtual environment, but this time answering “yes” when it asks whether your extension should have any settings. Since the settings use the same schema this will give you the boliterplate you need and then you can compare the two (or copy-paste your code into the new one).

Thank you. I will try making a new copier installation. The record of my setup answers does indicate that I did not say that it would include settings. This experience leads me to some things the Software Steering Committee might want to think about (and maybe already has):

  1. Proper set up to create a working extension for Jupyterlab seems to be very delicate because so many settings, file names, and paths have to align. The templates help, but it would be extremely helpful to have detailed documentation on every setting and how the directory structure needs to be set up to use each extension point. In the case of settings it appears that the following things have to be set/named properly:
  • location of the plugin schema *.json file(s).
  • name of schema directory in the jupyterlab: stanza in package.json.
  • add schema directory to the files: stanza in package.json.
  • proper form of string for plugin.id in .ts code. Proper syntax and how they relate to filenames is critical information.
  • plus more things.
  1. I have been putting off converting my old Jupyter extensions to work in Jupyterlab because the API seemed to be changing quite rapidly. I think things have settled a bit. Thus, I have decided to try to do the conversion. Assming I am correct about the settling of the API, I think it may be worth core Jupyter developers lowering their efforts towards new features or code improvements that lead to API changes and for 6 months to a year focus primarily on bugfixes and clarifying API documentation (aim for SciPy, Pandas, NumPy, jQuery and MDN level detail and clarity). I think this would facilitate more extensions and their longevity.

I am not primarily a programmer. I am a Chemistry Professor. However, because I work with custom instrumentation and novel datasets, I have repeatedly had to write code to interface to hardware, process data and provide a practical user interface. I am not particularly talented and have very limited time to devote to coding. However, I have used many different packages and languages. Getting everything lined up to use Jupyterlab as a base to build on has been much more difficult than anything I’ve encountered before (including the original Jupyter). I think the modularization of Jupyterlab is a good idea in terms of maintenance and minimization of code duplication. However, the API is very unclear to someone who does not have time to become a core developer level expert on the codebase.

Thanks for your help and your contributions to the wonderful tool that Jupyter is.

1 Like

Thank you for your feedback, it is very valuable to hear it. The JupyterLab API should be more stable now indeed. If you can share the repository with the code where you attempt to add menu in your extension I can take a look and will update the extension examples to include the missing bits.
If you would like to chat or have a short zoom call to debug this please drop me a message.

1 Like

@krassowski
Thank you for the offer of help. As I have limited time and have figured out how to make menus programmatically, I am going to ignore the schema method for now. I am on to my next problem. Which relates to CommandLinker.

Once I get the things I need working, I will revisit schema menus. I might then have more specific suggestions for inclusions in the examples.

Thanks,
Jonathan