{"id":622,"date":"2018-11-13T18:37:27","date_gmt":"2018-11-13T18:37:27","guid":{"rendered":"https:\/\/www.pyxll.com\/blog\/?p=622"},"modified":"2023-01-05T10:05:20","modified_gmt":"2023-01-05T10:05:20","slug":"how-to-customize-excel-context-menus","status":"publish","type":"post","link":"https:\/\/www.pyxll.com\/blog\/how-to-customize-excel-context-menus\/","title":{"rendered":"Customising Excel&#8217;s Context Menus"},"content":{"rendered":"\n<ol class=\"wp-block-list\"><li><a href=\"#intro\">Introduction<\/a><\/li><li><a href=\"#adding-buttons\">Adding a Button to the Cells Menu<\/a><\/li><li><a href=\"#dynamic-menus\">Dynamic Menus<\/a><\/li><li><a href=\"#other-controls\">Other Menu Controls<\/a><\/li><li><a href=\"#resources\">Resources<\/a><\/li><\/ol>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"intro\">Introduction<\/h2>\n\n\n\n<p><strong>Note:<\/strong><em> This tutorial requires <a href=\"https:\/\/www.pyxll.com\" target=\"_blank\" rel=\"noopener noreferrer\">PyXLL, the Python Excel Add-In<\/a>, &gt;= 4.0 and Excel &gt;= 2010.<\/em><\/p>\n\n\n\n<p>Context, or shortcut, menus in Excel are one of the most commonly used user interface features of Excel. The most well know context menu is the cells menu. When you right click on a worksheet cell or selection you&#8217;re presented with a menu with a range of options for copy and pasting, cell formatting and other functions.<\/p>\n\n\n\n<figure class=\"wp-block-image is-style-default\"><img decoding=\"async\" width=\"300\" height=\"247\" data-attachment-id=\"623\" data-permalink=\"https:\/\/www.pyxll.com\/blog\/how-to-customize-excel-context-menus\/cell-menu\/\" data-orig-file=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/cell-menu.png\" data-orig-size=\"846,696\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"cell-menu\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/cell-menu.png\" src=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/cell-menu-300x247.png\" alt=\"\" class=\"wp-image-623\" srcset=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/cell-menu-300x247.png 300w, https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/cell-menu-768x632.png 768w, https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/cell-menu.png 846w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/figure>\n\n\n\n<p>This post shows how to customize these context menus with using <a href=\"https:\/\/www.pyxll.com\" target=\"_blank\" rel=\"noopener noreferrer\">PyXLL, the Python Excel Add-In<\/a>, in versions of Excel from 2010 onward (including Office 365).<\/p>\n\n\n\n<p><a href=\"https:\/\/www.pyxll.com\" target=\"_blank\" rel=\"noopener noreferrer\">PyXLL<\/a> embeds Python in Excel, allowing you to write fully-featured Excel add-ins and replace VBA with Python.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"adding-buttons\">Adding a Button to the Cells Menu<\/h2>\n\n\n\n<p>Context menus are customized using the ribbon xml file specified in the pyxll.cfg config file. See <a href=\"https:\/\/www.pyxll.com\/docs\/userguide\/ribbon.html\" target=\"_blank\" rel=\"noopener noreferrer\">Customizing the Ribbon<\/a> in the PyXLL user guide for more details about configuring the ribbon.xml file.<\/p>\n\n\n\n<p>If you&#8217;ve not already installed PyXLL, see the <a href=\"https:\/\/www.pyxll.com\/docs\/introduction.html\" target=\"_blank\" rel=\"noopener noreferrer\">Introduction<\/a> to get started.<\/p>\n\n\n\n<p><strong>Note:<\/strong><em> Customizing the context menus using the ribbon.xml file is only possible in Excel 2010 and later versions (including Office 365). For that reason, the following will not work with Excel 2007 or earlier versions.<\/em><\/p>\n\n\n\n<p>The xml below shows how to add a button and a sub-menu containing some more buttons to the Excel Cells context menu. Copy and paste this code and save it to a file named <em>ribbon.xml<\/em> and add it to your pyxll.cfg file as follows:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n&#x5B;PYXLL]\nribbon = &amp;lt;path to ribbon.xml&gt;\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;\n&lt;customUI xmlns=&quot;http:\/\/schemas.microsoft.com\/office\/2009\/07\/customui&quot;&gt;\n    &lt;ribbon&gt;\n        &lt;!-- The ribbon and context menus are specified in the same file --&gt;\n    &lt;\/ribbon&gt;\n    &lt;contextMenus&gt;\n        &lt;contextMenu idMso=&quot;ContextMenuCell&quot;&gt;\n            &lt;button id=&quot;MyButton&quot; label=&quot;Toggle Case Upper\/Lower\/Proper&quot;\n                insertBeforeMso=&quot;Cut&quot;\n                onAction=&quot;context_menus.toggle_case&quot;\n                imageMso=&quot;HappyFace&quot;\/&gt;\n            &lt;menu id=&quot;MySubMenu&quot; label=&quot;Case Menu&quot; insertBeforeMso=&quot;Cut&quot;&gt;\n                &lt;button id=&quot;Menu1Button1&quot; label=&quot;Upper Case&quot;\n                    imageMso=&quot;U&quot;\n                    onAction=&quot;context_menus.toupper&quot;\/&gt;\n                &lt;button id=&quot;Menu1Button2&quot; label=&quot;Lower Case&quot;\n                    imageMso=&quot;L&quot;\n                    onAction=&quot;context_menus.tolower&quot;\/&gt;\n                &lt;button id=&quot;Menu1Button3&quot; label=&quot;Proper Case&quot;\n                    imageMso=&quot;P&quot;\n                    onAction=&quot;context_menus.toproper&quot;\/&gt;\n            &lt;\/menu&gt;\n        &lt;\/contextMenu&gt;\n   &lt;\/contextMenus&gt;\n&lt;\/customUI&gt;\n<\/pre><\/div>\n\n\n<p><strong>Note:<\/strong><em> The &#8220;http:\/\/schemas.microsoft.com\/office\/2009\/07\/customui&#8221; schema is important, and is different from the schema used in the example ribbon.xml file found in the PYXLL examples. Without this your context menus won&#8217;t appear.<\/em><\/p>\n\n\n\n<p>Once you&#8217;ve configured PyXLL with this ribbon.xml file, when you reload PyXLL or restart Excel you&#8217;ll see those menus added to the cells context menu.<\/p>\n\n\n\n<figure class=\"wp-block-image is-style-default\"><img decoding=\"async\" width=\"1024\" height=\"601\" data-attachment-id=\"637\" data-permalink=\"https:\/\/www.pyxll.com\/blog\/how-to-customize-excel-context-menus\/cell-menu-custom\/\" data-orig-file=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/cell-menu-custom.png\" data-orig-size=\"1042,612\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"cell-menu-custom\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/cell-menu-custom-1024x601.png\" src=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/cell-menu-custom-1024x601.png\" alt=\"\" class=\"wp-image-637\" srcset=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/cell-menu-custom-1024x601.png 1024w, https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/cell-menu-custom-300x176.png 300w, https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/cell-menu-custom-768x451.png 768w, https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/cell-menu-custom.png 1042w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>If you try selecting any of the newly added menu items, nothing happens! That&#8217;s because the Python functions that the menu items reference haven&#8217;t been written yet.<\/p>\n\n\n\n<p>On each menu item you&#8217;ll see an <em>onAction<\/em> tag. The value of those is the Python function that should be called when the menu item is selected. We need a new module called &#8220;context_menus.py&#8221; where we can write those functions. The module name isn&#8217;t important as long as it matches the xml, but we&#8217;ve used &#8220;context_menus&#8221; in the xml above.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nfrom pyxll import xl_app\n \n \ndef toggle_case(control):\n    \"\"\"Toggle the case of the currently selected cells\"\"\"\n    # get the Excel Application object\n    xl = xl_app()\n \n    # iterate over the currently selected cells\n    for cell in xl.Selection:\n        # get the cell value\n        value = cell.Value\n \n        # skip any cells that don't contain text\n        if not isinstance(value, str):\n            continue\n \n        # toggle between upper, lower and proper case\n        if value.isupper():\n            value = value.lower()\n        elif value.islower():\n            value = value.title()\n        else:\n            value = value.upper()\n \n        # set the modified value on the cell\n        cell.Value = value\n \n \ndef tolower(control):\n    \"\"\"Set the currently selected cells to lower case\"\"\"\n    # get the Excel Application object\n    xl = xl_app()\n \n    # iterate over the currently selected cells\n    for cell in xl.Selection:\n        # get the cell value\n        value = cell.Value\n \n        # skip any cells that don't contain text\n        if not isinstance(value, str):\n            continue\n \n        cell.Value = value.lower()\n \ndef toupper(control):\n    \"\"\"Set the currently selected cells to upper case\"\"\"\n    # get the Excel Application object\n    xl = xl_app()\n \n    # iterate over the currently selected cells\n    for cell in xl.Selection:\n        # get the cell value\n        value = cell.Value\n \n        # skip any cells that don't contain text\n        if not isinstance(value, str):\n            continue\n \n        cell.Value = value.upper()\n \n \ndef toproper(control):\n    \"\"\"Set the currently selected cells to 'proper' case\"\"\"\n    # get the Excel Application object\n    xl = xl_app()\n \n    # iterate over the currently selected cells\n    for cell in xl.Selection:\n        # get the cell value\n        value = cell.Value\n \n        # skip any cells that don't contain text\n        if not isinstance(value, str):\n            continue\n \n        cell.Value = value.title()\n<\/pre><\/div>\n\n\n<p>The &#8220;Excel.Application&#8221; object returned by &#8220;pyxll.xl_app()&#8221; is exactly the same as the Application object in VBA. It&#8217;s part of the Excel Object Model, and is exactly the same in Python as it is in VBA. Therefore, you can use it to automate Excel and do everything that you would previously have done using VBA.<\/p>\n\n\n\n<p>Make sure this new module is on the Python path in your pyxll.cfg file, and add it to the list of modules.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n&#x5B;PYXLL]\nribbon = ribbon.xml\nmodules =\n    context_menus\n \n&#x5B;PYTHON]\npythonpath =\n    &amp;lt;path to your Python code&gt;\n<\/pre><\/div>\n\n\n<figure class=\"wp-block-image is-style-default\"><img decoding=\"async\" width=\"1247\" height=\"620\" data-attachment-id=\"649\" data-permalink=\"https:\/\/www.pyxll.com\/blog\/how-to-customize-excel-context-menus\/context-menus-3\/\" data-orig-file=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/context-menus-2.gif\" data-orig-size=\"1247,620\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"context-menus\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/context-menus-2.gif\" src=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/context-menus-2.gif\" alt=\"\" class=\"wp-image-649\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"dynamic-menus\">Dynamic Menus<\/h2>\n\n\n\n<p>Rather than specify the items in a menu statically in the xml file, it&#8217;s possible to specify a function for Excel to call to get the menu items at run-time.<\/p>\n\n\n\n<p>Instead of the &#8220;contextMenu&#8221; element, a &#8220;dynamicMenu&#8221; element is used instead:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;\n&lt;customUI xmlns=&quot;http:\/\/schemas.microsoft.com\/office\/2009\/07\/customui&quot;&gt;\n    &lt;ribbon&gt;\n        &lt;!-- The ribbon and context menus can be specified in the same file --&gt;\n    &lt;\/ribbon&gt;\n    &lt;contextMenus&gt;\n        &lt;contextMenu idMso=&quot;ContextMenuCell&quot;&gt;\n            &lt;dynamicMenu id=&quot;MyDynamicMenu&quot;\n                label= &quot;My Dynamic Menu&quot;\n                imageMso=&quot;ChangeCase&quot;\n                insertBeforeMso=&quot;Cut&quot;\n                getContent=&quot;context_menus.dynamic_menu&quot;\/&gt;\n        &lt;\/contextMenu&gt;\n   &lt;\/contextMenus&gt;\n&lt;\/customUI&gt;\n<\/pre><\/div>\n\n\n<p>The &#8220;getContent&#8221; tag specifies the Python function to call to get the contents of the menu. The function returns an xml fragment that will be used to construct the menu.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\ndef dynamic_menu(control):\n    &quot;&quot;&quot;Return an xml fragment for the dynamic menu&quot;&quot;&quot;\n    xml = &quot;&quot;&quot;\n         &lt;menu xmlns=&quot;http:\/\/schemas.microsoft.com\/office\/2009\/07\/customui&quot;&gt;\n            &lt;button id=&quot;Menu2Button1&quot; label=&quot;Upper Case&quot;\n                imageMso=&quot;U&quot;\n                onAction=&quot;context_menus.toupper&quot;\/&gt;\n \n            &lt;button id=&quot;Menu2Button2&quot; label=&quot;Lower Case&quot;\n                imageMso=&quot;L&quot;\n                onAction=&quot;context_menus.tolower&quot;\/&gt;\n \n            &lt;button id=&quot;Menu2Button3&quot; label=&quot;Proper Case&quot;\n                imageMso=&quot;P&quot;\n                onAction=&quot;context_menus.toproper&quot;\/&gt;\n         &lt;\/menu&gt;\n    &quot;&quot;&quot;\n    return xml\n<\/pre><\/div>\n\n\n<p>The code above should go in the &#8220;context_menus&#8221; module as in the previous section. Rather than using a simple string as above, any of the Python xml packages could be used to generate the menu programmatically.<\/p>\n\n\n\n<p>For more details about the <em>dynamicMenu<\/em> element see the Microsoft documentation <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/dd944616(v=office.12).aspx\" target=\"_blank\" rel=\"noopener noreferrer\">2.2.19 dynamicMenu (Dynamic Menu)<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"other-controls\">Other Menu Controls<\/h2>\n\n\n\n<p>As well as buttons, used in the previous sections, other controls are available to use in context menus. The available control types are:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>control<\/li><li>button<\/li><li>checkBox<\/li><li>dynamicMenu<\/li><li>gallery<\/li><li>menu<\/li><li>menuSeparator<\/li><li>splitButton<\/li><li>toggleButton<\/li><\/ul>\n\n\n\n<p>For details of each control type, see the Microsoft Ribbon documentation <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/dd926139(v=office.12).aspx\" target=\"_blank\" rel=\"noopener noreferrer\">2.2 Elements<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"resources\">Resources<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li><a rel=\"noopener noreferrer\" href=\"https:\/\/github.com\/pyxll\/pyxll-examples\/tree\/master\/contextmenus\" target=\"_blank\">Source code for this post<\/a><\/li><li><a rel=\"noopener noreferrer\" href=\"https:\/\/www.pyxll.com\" target=\"_blank\">PyXLL, The Python Excel Add-In<\/a><\/li><li><a rel=\"noopener noreferrer\" href=\"https:\/\/www.pyxll.com\/docs\/userguide\/contextmenus.html\" target=\"_blank\">Custom context menus<\/a> in the PyXLL user guide<\/li><li>Microsoft <a rel=\"noopener noreferrer\" href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/dd926139(v=office.12).aspx\" target=\"_blank\">RibbonX Documentation<\/a><\/li><li>Custom context menus in VBA <a rel=\"noopener noreferrer\" href=\"https:\/\/www.rondebruin.nl\/win\/s2\/win014.htm\" target=\"_blank\">tutorial<\/a><\/li><li>List of <a href=\"https:\/\/bert-toolkit.com\/imagemso-list.html\" target=\"_blank\" rel=\"noreferrer noopener\">imageMSOs<\/a><\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Create convenient shortcut menus for common actions written in Python, accessed via Excel&#8217;s right click context menus.<\/p>\n","protected":false},"author":1,"featured_media":637,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[2],"tags":[6,136,137],"class_list":["post-622","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pyxll","tag-excel","tag-menus","tag-ribbon"],"acf":[],"jetpack_featured_media_url":"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2018\/09\/cell-menu-custom.png","jetpack_shortlink":"https:\/\/wp.me\/p7l3LP-a2","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/posts\/622","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/comments?post=622"}],"version-history":[{"count":1,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/posts\/622\/revisions"}],"predecessor-version":[{"id":1673,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/posts\/622\/revisions\/1673"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/media\/637"}],"wp:attachment":[{"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/media?parent=622"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/categories?post=622"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/tags?post=622"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}