diff --git a/common/eda_base_frame.cpp b/common/eda_base_frame.cpp
index ee3969ed60..f6db975c9e 100644
--- a/common/eda_base_frame.cpp
+++ b/common/eda_base_frame.cpp
@@ -454,15 +454,7 @@ void EDA_BASE_FRAME::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVars
 
     if( GetBitmapStore()->ThemeChanged() )
     {
-        ClearScaledBitmapCache();
-
-        wxAuiPaneInfoArray panes = m_auimgr.GetAllPanes();
-
-        for( size_t i = 0; i < panes.GetCount(); ++i )
-        {
-            if( ACTION_TOOLBAR* toolbar = dynamic_cast<ACTION_TOOLBAR*>( panes[i].window ) )
-                toolbar->RefreshBitmaps();
-        }
+        ThemeChanged();
     }
 
     if( GetMenuBar() )
@@ -474,6 +466,20 @@ void EDA_BASE_FRAME::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVars
 }
 
 
+void EDA_BASE_FRAME::ThemeChanged()
+{
+    ClearScaledBitmapCache();
+
+    wxAuiPaneInfoArray panes = m_auimgr.GetAllPanes();
+
+    for( size_t i = 0; i < panes.GetCount(); ++i )
+    {
+        if( ACTION_TOOLBAR* toolbar = dynamic_cast<ACTION_TOOLBAR*>( panes[i].window ) )
+            toolbar->RefreshBitmaps();
+    }
+}
+
+
 void EDA_BASE_FRAME::LoadWindowState( const wxString& aFileName )
 {
     if( !Pgm().GetCommonSettings()->m_Session.remember_open_files )
diff --git a/include/eda_base_frame.h b/include/eda_base_frame.h
index 6ce90ffa75..b0c3efb16a 100644
--- a/include/eda_base_frame.h
+++ b/include/eda_base_frame.h
@@ -480,6 +480,11 @@ public:
      */
     void CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged ) override;
 
+    /**
+     * Process light/dark theme change.
+     */
+    virtual void ThemeChanged();
+
     /**
      * Notification event that the project has changed.
      */
diff --git a/pcbnew/action_plugin.cpp b/pcbnew/action_plugin.cpp
index d7beb58090..bfb70fcca6 100644
--- a/pcbnew/action_plugin.cpp
+++ b/pcbnew/action_plugin.cpp
@@ -28,6 +28,8 @@
  */
 
 #include "action_plugin.h"
+#include "bitmaps.h"
+#include "bitmap_store.h"
 
 
 ACTION_PLUGIN::~ACTION_PLUGIN()
@@ -156,16 +158,17 @@ void ACTION_PLUGINS::register_action( ACTION_PLUGIN* aAction )
     }
 
     // Load icon if supplied
-    if( !aAction->GetIconFileName().IsEmpty() )
+    wxString icon_file_name = aAction->GetIconFileName( GetBitmapStore()->IsDarkTheme() );
+    if( !icon_file_name.IsEmpty() )
     {
         {
             wxLogNull eat_errors;
-            aAction->iconBitmap.LoadFile( aAction->GetIconFileName() , wxBITMAP_TYPE_PNG );
+            aAction->iconBitmap.LoadFile( icon_file_name, wxBITMAP_TYPE_PNG );
         }
 
         if ( !aAction->iconBitmap.IsOk() )
         {
-            wxLogVerbose( "Failed to load icon " + aAction->GetIconFileName() + " for action plugin " );
+            wxLogVerbose( "Failed to load icon " + icon_file_name + " for action plugin " );
         }
     }
 
diff --git a/pcbnew/action_plugin.h b/pcbnew/action_plugin.h
index 9ce31c1097..4ad815d45e 100644
--- a/pcbnew/action_plugin.h
+++ b/pcbnew/action_plugin.h
@@ -83,9 +83,10 @@ public:
 
     /**
      * Function GetIconFileName
+     * @param dark true if requesting dark theme icon
      * @return a path to icon for the action plugin button
      */
-    virtual wxString GetIconFileName() = 0;
+    virtual wxString GetIconFileName( bool dark ) = 0;
 
     /**
      * Function GetPluginPath
diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp
index b73b361fa5..df2ec58dbf 100644
--- a/pcbnew/pcb_edit_frame.cpp
+++ b/pcbnew/pcb_edit_frame.cpp
@@ -1688,6 +1688,14 @@ void PCB_EDIT_FRAME::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVars
 }
 
 
+void PCB_EDIT_FRAME::ThemeChanged()
+{
+    PCB_BASE_EDIT_FRAME::ThemeChanged();
+
+    PythonPluginsReload();
+}
+
+
 void PCB_EDIT_FRAME::ProjectChanged()
 {
     PythonSyncProjectName();
diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h
index 24459b1ff6..7b750a7b22 100644
--- a/pcbnew/pcb_edit_frame.h
+++ b/pcbnew/pcb_edit_frame.h
@@ -824,6 +824,11 @@ public:
      */
     void CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged ) override;
 
+    /**
+     * Called when light/dark theme is changed.
+     */
+    void ThemeChanged() override;
+
     void ProjectChanged() override;
 
     wxString GetCurrentFileName() const override;
diff --git a/pcbnew/swig/pcbnew_action_plugins.cpp b/pcbnew/swig/pcbnew_action_plugins.cpp
index 8ec92ed0de..07649bd3b3 100644
--- a/pcbnew/swig/pcbnew_action_plugins.cpp
+++ b/pcbnew/swig/pcbnew_action_plugins.cpp
@@ -138,11 +138,17 @@ bool PYTHON_ACTION_PLUGIN::GetShowToolbarButton()
 }
 
 
-wxString PYTHON_ACTION_PLUGIN::GetIconFileName()
+wxString PYTHON_ACTION_PLUGIN::GetIconFileName( bool dark )
 {
     PyLOCK lock;
 
-    return CallRetStrMethod( "GetIconFileName" );
+    PyObject* arglist = Py_BuildValue( "(i)", (int) dark );
+
+    wxString result = CallRetStrMethod( "GetIconFileName", arglist );
+
+    Py_DECREF( arglist );
+
+    return result;
 }
 
 
diff --git a/pcbnew/swig/pcbnew_action_plugins.h b/pcbnew/swig/pcbnew_action_plugins.h
index 3a56b5e9e8..5c65072179 100644
--- a/pcbnew/swig/pcbnew_action_plugins.h
+++ b/pcbnew/swig/pcbnew_action_plugins.h
@@ -52,7 +52,7 @@ public:
     wxString    GetName() override;
     wxString    GetDescription() override;
     bool        GetShowToolbarButton() override;
-    wxString    GetIconFileName() override;
+    wxString    GetIconFileName( bool dark ) override;
     wxString    GetPluginPath() override;
     void        Run() override;
     void*       GetObject() override;
diff --git a/scripting/kicadplugins.i b/scripting/kicadplugins.i
index 8bc0eda619..d93fc34ca6 100644
--- a/scripting/kicadplugins.i
+++ b/scripting/kicadplugins.i
@@ -658,6 +658,7 @@ class ActionPlugin(KiCadPlugin, object):
     def __init__( self ):
         KiCadPlugin.__init__( self )
         self.icon_file_name = ""
+        self.dark_icon_file_name = ""
         self.show_toolbar_button = False
         self.defaults()
 
@@ -678,8 +679,11 @@ class ActionPlugin(KiCadPlugin, object):
     def GetShowToolbarButton( self ):
         return self.show_toolbar_button
 
-    def GetIconFileName( self ):
-        return self.icon_file_name
+    def GetIconFileName( self, dark ):
+        if dark and self.dark_icon_file_name:
+            return self.dark_icon_file_name
+        else:
+            return self.icon_file_name
 
     def Run(self):
         return